001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.gravity.adapters;
022
023/**
024 * Adapted from Greg Wilkins code (Jetty).
025 * 
026 * @author William DRAI
027 */
028public class TopicId {
029
030    public final static String WILD = "*";
031    public final static String WILDWILD = "**";
032
033    private final static String[] ROOT = {};
034
035    private final String name;
036    private final String[] segments;
037    private final int wild;
038
039    public TopicId(String name) {
040        this.name = name;
041        if (name == null || name.length() == 0 || name.charAt(0) != '/')
042            throw new IllegalArgumentException("Illegal topic name: " + name);
043
044        if ("/".equals(name))
045            segments = ROOT;
046        else {
047            if (name.charAt(name.length() - 1) == '/')
048                throw new IllegalArgumentException("Illegal topic name (should not end with '/'): " + name);
049            segments = name.substring(1).split("\\Q/\\E", -1);
050        }
051
052        if (segments.length > 0) {
053            if (WILD.equals(segments[segments.length-1]))
054                wild = 1;
055            else if (WILDWILD.equals(segments[segments.length-1]))
056                wild = 2;
057            else
058                wild = 0;
059        }
060        else
061            wild = 0;
062    }
063
064    public boolean isWild() {
065        return wild > 0;
066    }
067
068    @Override
069    public boolean equals(Object obj) {
070        if (this == obj)
071            return true;
072
073        if (obj instanceof TopicId) {
074            TopicId other = (TopicId)obj;
075            if (isWild()) {
076                if (other.isWild())
077                    return this.name.equals(other.name);
078                return matches(other);
079            }
080            if (other.isWild())
081                return other.matches(this);
082            return name.equals(other.name);
083        }
084        else if (obj instanceof String) {
085            if (isWild())
086                return matches((String)obj);
087            return name.equals(obj);
088        }
089
090        return false;
091    }
092
093    public boolean matches(TopicId name) {
094        if (name.isWild())
095            return equals(name);
096
097        switch (wild) {
098            case 0:
099                return equals(name);
100            case 1:
101                if (name.segments.length != segments.length)
102                    return false;
103                for (int i = segments.length-1; i-- > 0; )
104                    if (!segments[i].equals(name.segments[i]))
105                        return false;
106                return true;
107
108            case 2:
109                if (name.segments.length < segments.length)
110                    return false;
111                for (int i = segments.length-1; i-- > 0; )
112                    if (!segments[i].equals(name.segments[i]))
113                        return false;
114                return true;
115        }
116        return false;
117    }
118
119    public boolean matches(String name) {
120        if (wild == 0)
121            return this.name.equals(name);
122
123        // TODO more efficient?
124        return matches(new TopicId(name));
125    }
126
127    @Override
128    public int hashCode() {
129        return name.hashCode();
130    }
131
132    @Override
133    public String toString() {
134        return name;
135    }
136
137    public int depth() {
138        return segments.length;
139    }
140
141    public boolean isParentOf(TopicId id) {
142        if (isWild() || depth() >= id.depth())
143            return false;
144
145        for (int i = segments.length-1; i-- >0; )
146            if (!segments[i].equals(id.segments[i]))
147                return false;
148
149        return true;
150    }
151
152    public String getSegment(int i) {
153        if (i > segments.length)
154            return null;
155        return segments[i];
156    }
157    
158    public static String normalize(String topicId) {
159        if (topicId == null)
160                return "/";
161        if (topicId.indexOf('.') >= 0)
162                topicId = topicId.replace('.', '/');
163        return (topicId.startsWith("/") ? topicId : ("/" + topicId));
164    }
165}