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 }