001/* Copyright 2011-2012 the original author or authors:
002 *
003 *    Marc Palmer (marc@grailsrocks.com)
004 *    Stéphane Maldini (smaldini@vmware.com)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.grails.plugin.platform.events;
019
020import groovy.lang.Closure;
021
022import java.io.Serializable;
023import java.lang.reflect.Method;
024
025/**
026 * @author Stephane Maldini <smaldini@vmware.com>
027 * @version 1.0
028 * @file
029 * @date 09/01/12
030 * @section DESCRIPTION
031 * <p/>
032 * format : namespace://topic:package.Class#method@hashCode
033 */
034public class ListenerId implements Serializable {
035    private static final String CLOSURE_METHOD_NAME = "call";
036    private static final String ID_CLASS_SEPARATOR = ":";
037    private static final String ID_METHOD_SEPARATOR = "#";
038    private static final String ID_HASHCODE_SEPARATOR = "@";
039    public static final String NAMESPACE_WILDCARD = "*";
040    public static final String ID_NAMESPACE_SEPARATOR = "://";
041
042    /*private static final Pattern idRegex = Pattern.compile(
043            "([^" + ID_NAMESPACE_SEPARATOR + "]*)?" +
044                    "("+ID_NAMESPACE_SEPARATOR +")?"+
045                    "([^" + ID_CLASS_SEPARATOR + "]*)?" +
046                    "(" + ID_CLASS_SEPARATOR + "([^" + ID_METHOD_SEPARATOR + "]*))?"
047                    + "(" + ID_METHOD_SEPARATOR + "([^" + ID_HASHCODE_SEPARATOR + "]*))?"
048                    + "(" + ID_HASHCODE_SEPARATOR + "(-?\\d*))?");
049*/
050    private String className;
051    private String methodName;
052    private String hashCode;
053    private String topic;
054
055    private String namespace;
056
057    public ListenerId(String namespace, String topic) {
058        this(namespace, topic, null, null, null);
059    }
060
061    public ListenerId(String namespace, String topic, String className, String methodName, String hashCode) {
062        this.className = className;
063        this.methodName = methodName;
064        this.hashCode = hashCode;
065        this.namespace = namespace;
066        if (topic != null && !topic.isEmpty()) {
067            this.topic = topic;
068        }
069    }
070
071    public String getTopic() {
072        return topic;
073    }
074
075    public void setTopic(String topic) {
076        this.topic = topic;
077    }
078
079    public String getNamespace() {
080        return namespace;
081    }
082
083    public void setNamespace(String namespace) {
084        this.namespace = namespace;
085    }
086
087    public String getClassName() {
088        return className;
089    }
090
091    public void setClassName(String className) {
092        this.className = className;
093    }
094
095    public String getMethodName() {
096        return methodName;
097    }
098
099    public void setMethodName(String methodName) {
100        this.methodName = methodName;
101    }
102
103    public String getHashCode() {
104        return hashCode;
105    }
106
107    public void setHashCode(String hashCode) {
108        this.hashCode = hashCode;
109    }
110
111    //format : namespace://topic:package.Class#method@hashCode
112    public String toString() {
113        return toStringWithoutHash()
114                + (hashCode != null ? ID_HASHCODE_SEPARATOR + hashCode : "");
115    }
116
117    // format : namespace://topic:package.Class#method
118    public String toStringWithoutHash() {
119        return (namespace != null ? namespace + ID_NAMESPACE_SEPARATOR : "") + (topic != null ? topic : "") + (className != null ? ID_CLASS_SEPARATOR + className : "")
120                + (methodName != null ? ID_METHOD_SEPARATOR + methodName : "");
121    }
122
123    static public ListenerId build(String namespace, String topic, Object target, Method callback) {
124        return new ListenerId(namespace, topic, target.getClass().getName(), callback.getName(), Integer.toString(target.hashCode()));
125    }
126
127    static public ListenerId build(String namespace, String topic, Class target, Method callback) {
128        return new ListenerId(namespace, topic, target.getName(), callback.getName(), null);
129    }
130
131    static public ListenerId build(String namespace, String topic, Closure target) {
132        return new ListenerId(namespace, topic, target.getClass().getName(), CLOSURE_METHOD_NAME, Integer.toString(target.hashCode()));
133    }
134
135    static public ListenerId parse(String id) {
136        //Matcher parsed = idRegex.matcher(id);
137        if (id != null) {
138            int namespaceIndex = id.indexOf(ID_NAMESPACE_SEPARATOR);
139            String _namespace = namespaceIndex != -1 ? id.substring(0, namespaceIndex) : null;
140            id = namespaceIndex != -1 ? id.substring(namespaceIndex + 3, id.length()) : id;
141
142            int classIndex = id.indexOf(ID_CLASS_SEPARATOR);
143            String _topic = id.substring(0, classIndex != -1 ? classIndex : id.length());
144            id = classIndex != -1 ? id.substring(classIndex + 1, id.length()) : id;
145
146            int methodIndex = id.indexOf(ID_METHOD_SEPARATOR);
147            String _class = classIndex != -1 ? id.substring(0, methodIndex != -1 ? methodIndex : id.length()) : null;
148            id = methodIndex != -1 ? id.substring(methodIndex + 1, id.length()) : id;
149
150            int hashcodeIndex = id.indexOf(ID_HASHCODE_SEPARATOR);
151            String _method = methodIndex != -1 ? id.substring(0, hashcodeIndex != -1 ? hashcodeIndex : id.length()) : null;
152            String _hashcode = hashcodeIndex != -1 ? id.substring(hashcodeIndex + 1, id.length()) : null;
153
154            return new ListenerId(
155                    _namespace,
156                    _topic,
157                    _class,
158                    _method,
159                    _hashcode
160            );
161        }
162
163        return null;
164    }
165
166    public static boolean matchesTopic(String source, String target, boolean checkTargetTopic){
167        int topicIdx = source.indexOf(NAMESPACE_WILDCARD);
168        int targetTopicIdx = target.indexOf(NAMESPACE_WILDCARD);
169        return checkTargetTopic && targetTopicIdx != -1 && source.startsWith(target.substring(0, targetTopicIdx)) ||
170                        topicIdx != -1 && target.startsWith(source.substring(0, topicIdx)) ||
171                        source.equals(target);
172    }
173
174    public static boolean matchesNamespace(String source, String target, boolean checkTargetTopic){
175        return source.equals(NAMESPACE_WILDCARD) ||
176                checkTargetTopic && target.equals(NAMESPACE_WILDCARD) ||
177                source.equalsIgnoreCase(target);
178    }
179
180    public boolean matches(ListenerId target) {
181        Boolean result = null;
182
183        if (this.namespace != null && target.getNamespace() != null) {
184            result = matchesNamespace(this.namespace, target.getNamespace(), true);
185        }
186
187        if (this.topic != null) {
188            result = result == null || result;
189            result &= matchesTopic(this.topic, target.getTopic(), true);
190        }
191
192        if (this.className != null) {
193            result = result == null || result;
194            result &= this.className.equals(target.getClassName());
195            if (this.methodName != null) {
196                result &= this.methodName.equals(target.getMethodName());
197                if (this.hashCode != null) {
198                    result &= this.hashCode.equals(target.getHashCode());
199                }
200            }
201        }
202
203        return result != null && result;
204    }
205
206    public boolean equals(String patternId) {
207        ListenerId listener = parse(patternId);
208        return matches(listener);
209    }
210
211    @Override
212    public boolean equals(Object o) {
213        if (this == o) return true;
214        if (o == null || getClass() != o.getClass()) return false;
215
216        ListenerId listener = (ListenerId) o;
217
218        return !(className != null ? !className.equals(listener.className) : listener.className != null) &&
219                !(namespace != null ? !namespace.equals(listener.namespace) : listener.namespace != null) &&
220                !(hashCode != null ? !hashCode.equals(listener.hashCode) : listener.hashCode != null) &&
221                !(methodName != null ? !methodName.equals(listener.methodName) : listener.methodName != null) &&
222                !(topic != null ? !topic.equals(listener.topic) : listener.topic != null);
223
224    }
225
226    @Override
227    public int hashCode() {
228        int result = className != null ? className.hashCode() : 0;
229        result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
230        result = 31 * result + (hashCode != null ? hashCode.hashCode() : 0);
231        result = 31 * result + (topic != null ? topic.hashCode() : 0);
232        result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
233        return result;
234    }
235}