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     */
022    package org.granite.gravity.gae;
023    
024    
025    /**
026     * Adapted from Greg Wilkins code (Jetty).
027     * 
028     * @author William DRAI
029     */
030    public class GAETopicId {
031            
032            public final static String WILD = "*";
033        public final static String WILDWILD = "**";
034    
035        private final static String[] ROOT = {};
036    
037        private final String name;
038        private final String[] segments;
039        private final int wild;
040    
041        public GAETopicId(String name) {
042            this.name = name;
043            if (name == null || name.length() == 0 || name.charAt(0) != '/')
044                throw new IllegalArgumentException("Illegal topic name: " + name);
045    
046            if ("/".equals(name))
047                segments = ROOT;
048            else {
049                if (name.charAt(name.length() - 1) == '/')
050                    throw new IllegalArgumentException("Illegal topic name (should not end with '/'): " + name);
051                segments = name.substring(1).split("\\Q/\\E", -1);
052            }
053    
054            if (segments.length > 0) {
055                if (WILD.equals(segments[segments.length-1]))
056                    wild = 1;
057                else if (WILDWILD.equals(segments[segments.length-1]))
058                    wild = 2;
059                else
060                    wild = 0;
061            }
062            else
063                wild = 0;
064        }
065    
066        public boolean isWild() {
067            return wild > 0;
068        }
069    
070        @Override
071        public boolean equals(Object obj) {
072            if (this == obj)
073                return true;
074    
075            if (obj instanceof GAETopicId) {
076                GAETopicId other = (GAETopicId)obj;
077                if (isWild()) {
078                    if (other.isWild())
079                        return this.name.equals(other.name);
080                    return matches(other);
081                }
082                if (other.isWild())
083                    return other.matches(this);
084                return name.equals(other.name);
085            }
086            else if (obj instanceof String) {
087                if (isWild())
088                    return matches((String)obj);
089                return name.equals(obj);
090            }
091    
092            return false;
093        }
094    
095        public boolean matches(GAETopicId name) {
096            if (name.isWild())
097                return equals(name);
098    
099            switch (wild) {
100                case 0:
101                    return equals(name);
102                case 1:
103                    if (name.segments.length != segments.length)
104                        return false;
105                    for (int i = segments.length-1; i-- > 0; )
106                        if (!segments[i].equals(name.segments[i]))
107                            return false;
108                    return true;
109    
110                case 2:
111                    if (name.segments.length < segments.length)
112                        return false;
113                    for (int i = segments.length-1; i-- > 0; )
114                        if (!segments[i].equals(name.segments[i]))
115                            return false;
116                    return true;
117            }
118            return false;
119        }
120    
121        public boolean matches(String name) {
122            if (wild == 0)
123                return this.name.equals(name);
124    
125            // TODO more efficient?
126            return matches(new GAETopicId(name));
127        }
128    
129        @Override
130        public int hashCode() {
131            return name.hashCode();
132        }
133    
134        @Override
135        public String toString() {
136            return name;
137        }
138    
139        public int depth() {
140            return segments.length;
141        }
142    
143        public boolean isParentOf(GAETopicId id) {
144            if (isWild() || depth() >= id.depth())
145                return false;
146    
147            for (int i = segments.length-1; i-- >0; )
148                if (!segments[i].equals(id.segments[i]))
149                    return false;
150    
151            return true;
152        }
153    
154        public String getSegment(int i) {
155            if (i > segments.length)
156                return null;
157            return segments[i];
158        }
159        
160        public static String normalize(String topicId) {
161            if (topicId == null)
162                    return "/";
163            if (topicId.indexOf('.') >= 0)
164                    topicId = topicId.replace('.', '/');
165            return (topicId.startsWith("/") ? topicId : ("/" + topicId));
166        }
167    }