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