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    
021    package org.granite.gravity.adapters;
022    
023    /**
024     * Adapted from Greg Wilkins code (Jetty).
025     * 
026     * @author William DRAI
027     */
028    public 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    }