001 package org.nakedobjects.applib;
002
003 import org.nakedobjects.applib.util.NameUtils;
004
005
006 public class Identifier implements Comparable<Identifier> {
007
008 /**
009 * What type of feature this identifies.
010 */
011 public static enum Type {
012 CLASS, PROPERTY_OR_COLLECTION, ACTION
013 }
014
015 public static enum Depth {
016 CLASS {
017 @Override
018 public String toIdentityString(Identifier identifier) {
019 return identifier.toClassIdentityString();
020 }
021 },
022 CLASS_MEMBERNAME {
023 @Override
024 public String toIdentityString(Identifier identifier) {
025 return identifier.toClassAndNameIdentityString();
026 }
027 },
028 CLASS_MEMBERNAME_PARMS {
029 @Override
030 public String toIdentityString(Identifier identifier) {
031 return identifier.toFullIdentityString();
032 }
033 },
034 MEMBERNAME_ONLY {
035 @Override
036 public String toIdentityString(Identifier identifier) {
037 return identifier.toNameIdentityString();
038 }
039 },
040 PARMS_ONLY {
041 @Override
042 public String toIdentityString(Identifier identifier) {
043 return identifier.toParmsIdentityString();
044 }
045 };
046 public abstract String toIdentityString(Identifier identifier);
047 }
048
049 public static Depth CLASS = Depth.CLASS;
050 public static Depth CLASS_MEMBERNAME = Depth.CLASS_MEMBERNAME;
051 public static Depth CLASS_MEMBERNAME_PARMS = Depth.CLASS_MEMBERNAME_PARMS;
052 public static Depth MEMBERNAME_ONLY = Depth.MEMBERNAME_ONLY;
053 public static Depth PARMS_ONLY = Depth.PARMS_ONLY;
054
055
056 // ///////////////////////////////////////////////////////////////////////////
057 // Factory methods
058 // ///////////////////////////////////////////////////////////////////////////
059
060 public static Identifier classIdentifier(final Class<?> cls) {
061 return classIdentifier(cls.getName());
062 }
063
064 public static Identifier classIdentifier(final String className) {
065 return new Identifier(className, "", new String[] {}, Type.CLASS);
066 }
067
068 public static Identifier propertyOrCollectionIdentifier(final Class<?> declaringClass, final String propertyOrCollectionName) {
069 return propertyOrCollectionIdentifier(declaringClass.getCanonicalName(), propertyOrCollectionName);
070 }
071
072 public static Identifier propertyOrCollectionIdentifier(final String declaringClassName, final String propertyOrCollectionName) {
073 return new Identifier(declaringClassName, propertyOrCollectionName, new String[] {}, Type.PROPERTY_OR_COLLECTION);
074 }
075
076 public static Identifier actionIdentifier(
077 final Class<?> declaringClass,
078 final String actionName,
079 final Class<?>[] parameterClasses) {
080 return actionIdentifier(declaringClass.getCanonicalName(), actionName, toParameterStringArray(parameterClasses));
081 }
082
083 public static Identifier actionIdentifier(
084 final String declaringClassName,
085 final String actionName,
086 final Class<?>[] parameterClasses) {
087 return actionIdentifier(declaringClassName, actionName, toParameterStringArray(parameterClasses));
088 }
089
090 public static Identifier actionIdentifier(
091 final String declaringClassName,
092 final String actionName,
093 final String[] parameterClassNames) {
094 return new Identifier(declaringClassName, actionName, parameterClassNames, Type.ACTION);
095 }
096
097 /**
098 * Helper, used within contructor chaining
099 */
100 private static String[] toParameterStringArray(final Class<?>[] parameterClasses) {
101 final String[] parameters = new String[parameterClasses == null ? 0 : parameterClasses.length];
102 for (int i = 0; i < parameters.length; i++) {
103 parameters[i] = parameterClasses[i].getName();
104 }
105 return parameters;
106 }
107
108 // ///////////////////////////////////////////////////////////////////////////
109 // Instance variables
110 // ///////////////////////////////////////////////////////////////////////////
111
112 private final String className;
113 private final String memberName;
114 private final String[] parameterNames;
115 private final Type type;
116 private String identityString;
117
118 /**
119 * Caching of {@link #toString()}, for performance.
120 */
121 private String asString = null;
122
123 // ///////////////////////////////////////////////////////////////////////////
124 // Constructor
125 // ///////////////////////////////////////////////////////////////////////////
126
127 private Identifier(final String className, final String memberName, final String[] parameterNames, final Type type) {
128 this.className = className;
129 this.memberName = memberName;
130 this.parameterNames = parameterNames;
131 this.type = type;
132 }
133
134 public String getClassName() {
135 return className;
136 }
137
138 public String getMemberName() {
139 return memberName;
140 }
141
142 public String getMemberNaturalName() {
143 return NameUtils.naturalName(memberName);
144 }
145
146 public String[] getMemberParameterNames() {
147 return parameterNames;
148 }
149
150 public String[] getMemberParameterNaturalNames() {
151 return NameUtils.naturalNames(parameterNames);
152 }
153
154 public Type getType() {
155 return type;
156 }
157
158 /**
159 * Convenience method.
160 *
161 * @return
162 */
163 public boolean isPropertyOrCollection() {
164 return type == Type.PROPERTY_OR_COLLECTION;
165 }
166
167 // ///////////////////////////////////////////////////////////////////////////
168 // toXxxString
169 // ///////////////////////////////////////////////////////////////////////////
170
171 public String toIdentityString(final Depth depth) {
172 return depth.toIdentityString(this);
173 }
174
175 public String toClassIdentityString() {
176 return toClassIdentityString(new StringBuilder()).toString();
177 }
178
179 public StringBuilder toClassIdentityString(StringBuilder buf) {
180 return buf.append(className);
181 }
182
183
184 public String toNameIdentityString() {
185 return toNameIdentityString(new StringBuilder()).toString();
186 }
187
188 public StringBuilder toNameIdentityString(StringBuilder buf) {
189 return buf.append(memberName);
190 }
191
192
193 public String toClassAndNameIdentityString() {
194 return toClassAndNameIdentityString(new StringBuilder()).toString();
195 }
196
197 public StringBuilder toClassAndNameIdentityString(StringBuilder buf) {
198 return toClassIdentityString(buf).append("#").append(memberName);
199 }
200
201
202 public String toParmsIdentityString() {
203 return toParmsIdentityString(new StringBuilder()).toString();
204 }
205
206 public StringBuilder toParmsIdentityString(StringBuilder buf) {
207 if (type == Type.ACTION) {
208 buf.append('(');
209 for (int i = 0; i < parameterNames.length; i++) {
210 if (i > 0) {
211 buf.append(",");
212 }
213 buf.append(parameterNames[i]);
214 }
215 buf.append(')');
216 }
217 return buf;
218 }
219
220 public String toNameParmsIdentityString() {
221 return getMemberName() + toParmsIdentityString();
222 }
223
224 public StringBuilder toNameParmsIdentityString(StringBuilder buf) {
225 buf.append(getMemberName());
226 toParmsIdentityString(buf);
227 return buf;
228 }
229
230 public String toFullIdentityString() {
231 if (identityString == null) {
232 if (memberName.length() == 0) {
233 identityString = toClassIdentityString();
234 } else {
235 final StringBuilder buf = new StringBuilder();
236 toClassAndNameIdentityString(buf);
237 toParmsIdentityString(buf);
238 identityString = buf.toString();
239 }
240 }
241 return identityString;
242 }
243
244 // ///////////////////////////////////////////////////////////////////////////
245 // compareTo
246 // ///////////////////////////////////////////////////////////////////////////
247
248 public int compareTo(final Identifier o2) {
249 return toString().compareTo(o2.toString());
250 }
251
252 // ///////////////////////////////////////////////////////////////////////////
253 // equals, hashCode
254 // ///////////////////////////////////////////////////////////////////////////
255
256 /**
257 * REVIEW: why not just compare the {@link #toString()} representations?
258 */
259 @Override
260 public boolean equals(final Object obj) {
261 if (this == obj) {
262 return true;
263 }
264 if (!(obj instanceof Identifier)) {
265 return false;
266 }
267 final Identifier other = (Identifier) obj;
268 return equals(other);
269 }
270
271 public boolean equals(final Identifier other) {
272 return equals(other.className, className) &&
273 equals(other.memberName, other.memberName) &&
274 equals(other.parameterNames, parameterNames);
275 }
276
277 private boolean equals(final String a, final String b) {
278 if (a == b) {
279 return true;
280 }
281
282 if (a != null) {
283 return a.equals(b);
284 }
285
286 return false;
287 }
288
289 private boolean equals(final String[] a, final String[] b) {
290 if (a == null && b == null) {
291 return true;
292 }
293 if (a == null && b != null) {
294 return false;
295 }
296 if (a != null && b == null) {
297 return false;
298 }
299 if (a.length != b.length) {
300 return false;
301 }
302 for (int i = 0; i < b.length; i++) {
303 if (!a[i].equals(b[i])) {
304 return false;
305 }
306 }
307 return true;
308 }
309
310 @Override
311 public int hashCode() {
312 return toString().hashCode();
313 }
314
315 // ///////////////////////////////////////////////////////////////////////////
316 // toString
317 // ///////////////////////////////////////////////////////////////////////////
318
319 @Override
320 public String toString() {
321 if (asString == null) {
322 final StringBuilder buf = new StringBuilder();
323 buf.append(className);
324 buf.append('#');
325 buf.append(memberName);
326 buf.append('(');
327 for (int i = 0; i < parameterNames.length; i++) {
328 if (i > 0) {
329 buf.append(", ");
330 }
331 buf.append(parameterNames[i]);
332 }
333 buf.append(')');
334 asString = buf.toString();
335 }
336 return asString;
337 }
338
339 }
340 // Copyright (c) Naked Objects Group Ltd.