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