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.gae;
022    
023    
024    /**
025     * Adapted from Greg Wilkins code (Jetty).
026     * 
027     * @author William DRAI
028     */
029    public class GAETopicId {
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 GAETopicId(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 GAETopicId) {
075                GAETopicId other = (GAETopicId)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(GAETopicId 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 GAETopicId(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(GAETopicId 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    }