KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > ui > nodes > elements > ElementFormat


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.ui.nodes.elements;
21
22 import java.text.*;
23 import java.util.*;
24 import java.io.*;
25 import java.lang.reflect.Modifier JavaDoc;
26
27 import org.openide.util.NbBundle;
28 import org.openide.src.ElementProperties;
29 import org.netbeans.jmi.javamodel.*;
30 import org.netbeans.modules.java.ui.nodes.editors.IdentifierArrayEditor;
31 import org.netbeans.modules.java.ui.nodes.editors.MethodParameterArrayEditor;
32 import org.netbeans.modules.java.ui.nodes.editors.TypeParameterArrayEditor;
33
34 import javax.jmi.reflect.JmiException;
35 import org.netbeans.modules.javacore.api.JavaModel;
36
37 /** A format used to print members of the source hierarchy.
38 * It is sometimes used for code generation of elements, and also
39 * for formatting the display names of the nodes representing
40 * the hierarchy.
41 * <P>
42 *
43 * This format is similar to {@link MessageFormat}.
44 * It also uses special characters in the pattern and replaces them with strings,
45 * depending on the code.
46 * <P>
47 * For example:
48 * <p><CODE><PRE>
49 * ElementFormat fmt = new ElementFormat ("{m} {r} {n} ({p})");
50 * MethodElement method = getMethodSomewhere ();
51 * System.out.println (fmt.format (method));
52 * </PRE></CODE>
53 * <p>...should print something like this: <code>"public int method(int,char)"</code>
54 *
55 * <p>The substitution codes are:
56 * <UL>
57 * <LI> <code>{m}</code> Modifiers
58 * <LI> <code>{M}</code> Modifier class or interface
59 * <LI> <code>{n}</code> Name
60 * <LI> <code>{C}</code> Name of class (with all outerclasses)
61 * <LI> <code>{f}</code> Full name of element with package
62 * <LI> <code>{t}</code> Type
63 * <LI> <code>{T}</code> Type Parameters
64 * <LI> <code>{r}</code> Return type
65 * <LI> <code>{s}</code> Superclass
66 * <LI> <code>{c}</code> Static (for initializers)
67 * <LI> <code>{p}</code> Parameters with types but not variable names (e.g. <code>"int,char"</code>).
68 * <LI> <code>{a}</code> Parameters with types and names (e.g. <code>"int x,char c"</code>).
69 * <LI> <code>{i}</code> Interfaces
70 * <LI> <code>{e}</code> Exceptions
71 * </UL>
72 *
73 * <P>
74 * The following table shows which codes may be used
75 * to format which kinds of element. An asterisk means
76 * the code may be used, a hyphen means it cannot:
77 *
78 * <p><CODE><PRE>
79 * character | m n f C t r s c p a i e M T
80 * -------------------------------------------------------
81 * Initializer | - - - - - - - * - - - - - -
82 * Field | * * * - * - - - - - - - - -
83 * Constructor | * * * - - - - - * * - * - -
84 * Method | * * * - - * - - * * - * - *
85 * Class | * * * * - - * - - - * - * *
86 * Interface | * * * * - - - - - - * - * *
87 * Enumeration | * * * * - - - - - - * - - *
88 * Constants | - * * - * - - - * * - - - -
89 * Ann Type | * * * * - - - - - - - - - -
90 * </PRE></CODE>
91 *
92 * <p>The grammar for expressions:
93 *
94 * <p><code><pre>
95 * messageFormatPattern := string ( "{" messageFormatElement "}" string )*
96 *
97 * messageFormatElement := simple_argument { "," prefix "," suffix }
98 *
99 * messageFormatElement := array_argument { "," prefix "," suffix { "," delim } }
100 *
101 * simple_argument := "m" | "n" | "f" | "C" | "t" | "r" | "s" | "c" | "M"
102 *
103 * array_argument := "p" | "a" | "i" | "e" | "T"
104 *
105 * prefix := string
106 *
107 * suffix := string
108 *
109 * delim := string
110 *
111 * </pre></code>
112 *
113 * <p>Comments on the previous grammar:
114 * <UL>
115 * <LI> <code>simple_argument</code> - arguments which are replaced by a single string
116 * <LI> <code>array_argument</code> - arguments for arrays (parameters, ...)
117 * <LI> <code>prefix</code> - prefix before the format element if nonempty
118 * <LI> <code>suffix</code> - suffix after the format element if nonempty
119 * <LI> <code>delim</code> - delimiter between the members of the array
120 * <LI> <code>string</code> - a bare string, or enclosed in double quotes if necessary
121 * (e.g. if it contains a comma)
122 * </UL>
123 *
124 * <P>
125 * Example formats:
126 * <UL>
127 * <LI> For a method which doesn't throw any exceptions: <code>{e,throws ,}</code> => <code>""</code>
128 * <LI> For a method which throws <code>IOException</code>: <code>{e,throws ,</code>} => <code>"throws IOException"</code>
129 * <LI> Method parameters #1: <code>{p,,,-}</code> => <code>"int-int-int"</code>
130 * <LI> Method parameters #2: <code>{p,(,),", "}</code> => <code>"(int, int, int)"</code>
131 * </UL>
132 * <p>The default delimiter is a comma.
133 * <p>This class <em>currently</em> has a default property editor
134 * in the property editor search path for the IDE.
135 *
136 * @author Petr Hamernik
137 */

138 public final class ElementFormat extends Format {
139
140     // ============== Static part =================================
141

142     /** Serial UID */
143     static final long serialVersionUID = 3775521938640169753L;
144
145     /** Magic characters for all kinds of the formating tags.
146     * The position of the characters is used as index to the following array.
147     */

148     private static final String JavaDoc PROPERTIES_NAMES_INDEX = "mnfCtrscpaieMT"; // NOI18N
149

150     /** Array of names of all kinds properties which could be included
151     * in the pattern string.
152     */

153     private static final String JavaDoc[] PROPERTIES_NAMES = {
154         ElementProperties.PROP_MODIFIERS, //m
155
ElementProperties.PROP_NAME, //n
156
ElementProperties.PROP_NAME, //f
157
ElementProperties.PROP_NAME, //C
158
ElementProperties.PROP_TYPE, //t
159
ElementProperties.PROP_RETURN, //r
160
ElementProperties.PROP_SUPERCLASS, //s
161
ElementProperties.PROP_STATIC, //c
162
ElementProperties.PROP_PARAMETERS, //p
163
ElementProperties.PROP_PARAMETERS, //a
164
ElementProperties.PROP_INTERFACES, //i
165
ElementProperties.PROP_EXCEPTIONS, //e
166
ElementProperties.PROP_MODIFIERS, //M
167
ElementProperties2.PROP_TYPE_PARAMETERS //T
168
};
169
170     /** Status constants for the parser. */
171     private static final byte STATUS_OUTSIDE = 0;
172     private static final byte STATUS_INSIDE = 1;
173     private static final byte STATUS_RBRACE = 2;
174
175     // ================ Non-static part ===============================
176

177     /** Pattern - the string which is given in the constructor. */
178     private String JavaDoc pattern;
179
180     /** The current value of "source" property */ // NOI18N
181
private boolean source;
182
183     /** List of parts of the formated string. Elements of this list are
184     * either String objects either Tag.
185     */

186     private transient LinkedList list;
187
188     // ================ Public methods =================================
189

190     /** Create a new format.
191     * See documentation for the class for the syntax of the format argument.
192     * @param pattern the pattern describing the format
193     */

194     public ElementFormat(String JavaDoc pattern) {
195         applyPattern(pattern);
196         source = true;
197     }
198
199     /** Set whether the formating is used for code generation.
200     * Default value is <CODE>true</CODE>.
201     * @param source <CODE>true</CODE> means that all Identifier and Type objects
202     * used in formating are evaluated by <CODE>getSourceName</CODE>
203     * method. Otherwise (<CODE>false</CODE>) the getFullName() is
204     * called.
205     */

206     public void setSourceFormat(boolean source) {
207         this.source = source;
208     }
209
210     /** Test if this format generate strings in source format or fully
211     * qualified format.
212     * XXX nowhere used
213     * @return the source flag
214     */

215     public boolean isSourceFormat() {
216         return source;
217     }
218
219     /** Get the pattern.
220     * @return the current pattern
221     */

222     public String JavaDoc getPattern() {
223         return pattern;
224     }
225
226     /** Format an object.
227     * @param o should be an {@link Element}
228     * @param toAppendTo the string buffer to format to
229     * @param pos currently ignored
230     * @return the same string buffer it was passed (for convenient chaining)
231     * @throws IllegalArgumentException if the object was not really an <code>Element</code>
232     */

233     public StringBuffer JavaDoc format(Object JavaDoc o, StringBuffer JavaDoc toAppendTo, FieldPosition pos) {
234         try {
235             JavaModel.getJavaRepository().beginTrans(false);
236             try {
237                 Element element = (Element) o;
238                 JavaModel.setClassPath(element.getResource());
239                 Iterator it = list.iterator();
240                 while (it.hasNext()) {
241                     Object JavaDoc obj = it.next();
242                     if (obj instanceof String JavaDoc) {
243                         toAppendTo.append((String JavaDoc)obj);
244                     }
245                     else {
246                         ((Tag)obj).format(element, toAppendTo);
247                     }
248                 }
249                 return toAppendTo;
250             } finally {
251                 JavaModel.getJavaRepository().endTrans();
252             }
253         } catch (ClassCastException JavaDoc e) {
254             IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badArgument")); // NOI18N
255
iae.initCause(e);
256             throw iae;
257         } catch (JmiException e) {
258             IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc("format failed"); // NOI18N
259
iae.initCause(e);
260             throw iae;
261         }
262     }
263
264     /** Formats an element.
265     * @param element the element to be printed
266     * @return the formatted string using the pattern
267     */

268     public String JavaDoc format(Element element) {
269         return format(element, new StringBuffer JavaDoc(), null).toString();
270     }
271
272     /** Test whether a property could affect the formatting.
273     * I.e., if that property would be read due to one of the control codes in the pattern.
274     * @param prop the property name from {@link ElementProperties}
275     * @return <code>true</code> if so
276     */

277     public boolean dependsOnProperty(String JavaDoc prop) {
278         Iterator it = list.iterator();
279         while (it.hasNext()) {
280             Object JavaDoc obj = it.next();
281             if (obj instanceof Tag) {
282                 int index = PROPERTIES_NAMES_INDEX.indexOf(((Tag)obj).kind);
283                 if (PROPERTIES_NAMES[index].equals(prop))
284                     return true;
285             }
286         }
287         return false;
288     }
289
290     /** Don't parse objects.
291     * @param source ignored
292     * @param status ignored
293     * @return <code>null</code> in the default implementation
294     */

295     public Object JavaDoc parseObject (String JavaDoc source, ParsePosition status) {
296         return null;
297     }
298
299     /** Reads the object and initialize fields. */
300     private void readObject(ObjectInputStream s) throws ClassNotFoundException JavaDoc, IOException {
301         s.defaultReadObject();
302         applyPattern(pattern);
303     }
304
305     // ====================== Private part ===================================
306

307     /** Parse the pattern. */
308     private void applyPattern(String JavaDoc pattern) {
309         this.pattern = pattern;
310         list = new LinkedList();
311
312         byte status = STATUS_OUTSIDE;
313         StringTokenizer tokenizer = new StringTokenizer(pattern, "{}", true); // NOI18N
314
while (tokenizer.hasMoreTokens()) {
315             String JavaDoc token = tokenizer.nextToken();
316             switch (status) {
317
318             case STATUS_OUTSIDE:
319                 if (token.equals("}")) // NOI18N
320
throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
321
if (token.equals("{")) // NOI18N
322
status = STATUS_INSIDE;
323                 else
324                     list.add(token);
325                 break;
326
327             case STATUS_INSIDE:
328                 if ((token.equals("{")) || (token.equals("}"))) // NOI18N
329
throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
330
list.add(createTag(token));
331                 status = STATUS_RBRACE;
332                 break;
333
334             case STATUS_RBRACE:
335                 if (!token.equals("}")) // NOI18N
336
throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
337
status = STATUS_OUTSIDE;
338                 break;
339
340             }
341         }
342     }
343
344     /** Creates the appropriate tag for the given String.
345     * @param s The string which is between the brackets in the pattern.
346     * @return the tag object.
347     */

348     private Tag createTag(String JavaDoc s) {
349         if (s.length() > 0) {
350             char c = s.charAt(0);
351             String JavaDoc[] params = new String JavaDoc[0];
352
353             if (s.length() > 1) {
354                 if ((s.length() < 2) || (s.charAt(1) != ','))
355                     throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
356
params = parseParams(s.substring(2));
357             }
358
359             if ("mnfCtrscM".indexOf(c) != -1) { // NOI18N
360
switch (params.length) {
361                 case 0: return new Tag(c, "", ""); // NOI18N
362
case 2: return new Tag(c, params[0], params[1]);
363                 }
364             }
365             else if ("paieT".indexOf(c) != -1) { // NOI18N
366
switch (params.length) {
367                 case 0: return new ArrayTag(c, "", "", ", "); // NOI18N
368
case 2: return new ArrayTag(c, params[0], params[1], ", "); // NOI18N
369
case 3: return new ArrayTag(c, params[0], params[1], params[2]);
370                 }
371             }
372         }
373         throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
374
}
375
376     /** Parse the parameters of the tag.
377     * @param s string of the params delimited by commas
378     * @return the array of the params.
379     */

380     private String JavaDoc[] parseParams(String JavaDoc s) {
381         StringTokenizer tokenizer = new StringTokenizer(s, ",", true); // NOI18N
382
ArrayList list = new ArrayList();
383         StringBuffer JavaDoc token = new StringBuffer JavaDoc();
384         boolean comma = false;
385         boolean inString = false;
386
387         while (tokenizer.hasMoreTokens()) {
388             String JavaDoc t = tokenizer.nextToken();
389
390             if (inString) {
391                 token.append(t);
392                 if (t.endsWith("\"")) { // NOI18N
393
if (token.length() > 1)
394                         token.setLength(token.length() - 1);
395                     list.add(token.toString());
396                     token.setLength(0);
397                     inString = false;
398                     comma = true;
399                 }
400                 continue;
401             }
402
403             if (t.equals(",")) { // NOI18N
404
if (comma)
405                     comma = false;
406                 else
407                     list.add(""); // NOI18N
408
continue;
409             }
410
411             if (comma)
412                 throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
413

414             String JavaDoc stringToAdd = t;
415
416             if (t.startsWith("\"")) { // NOI18N
417
if ((t.endsWith("\"")) && (t.length() > 1)) { // NOI18N
418
stringToAdd = (t.length() <= 2) ? "" : t.substring(1, t.length() - 1); // NOI18N
419
}
420                 else {
421                     token.append(t.substring(1));
422                     inString = true;
423                     continue;
424                 }
425             }
426
427             list.add(stringToAdd);
428             comma = true;
429             token.setLength(0);
430         }
431         if (!comma)
432             list.add(""); // NOI18N
433

434         String JavaDoc[] ret = new String JavaDoc[list.size()];
435         list.toArray(ret);
436         return ret;
437     }
438
439     /**
440      * gets short name of the element
441      * @param el element
442      * @return shart variant of element's name
443      * @throws JmiException
444      */

445     public static String JavaDoc elementName(NamedElement el) throws JmiException {
446         if (el instanceof JavaClass) {
447             return ((JavaClass) el).getSimpleName();
448         }
449         
450         if (el instanceof Constructor) {
451             JavaClass jc = (JavaClass) ((Constructor) el).getDeclaringClass();
452             return jc.getSimpleName();
453         }
454         
455         return el.getName();
456     }
457     
458     private static String JavaDoc elementFullName(NamedElement el) throws JmiException {
459         if (el instanceof JavaClass) {
460             return el.getName();
461         }
462         
463         if (!(el instanceof ClassMember)) {
464             return elementName(el);
465         }
466         
467         JavaClass jc = (JavaClass) ((ClassMember) el).getDeclaringClass();
468         StringBuffer JavaDoc fullName = new StringBuffer JavaDoc(30).append(jc.getName()).append('.');
469         if (el instanceof Constructor) {
470             fullName.append(jc.getSimpleName());
471         } else {
472             fullName.append(el.getName());
473         }
474         return fullName.toString();
475     }
476     
477     private static void resolveClassName(JavaClass jc, StringBuffer JavaDoc sb) {
478         ClassDefinition cd = jc.getDeclaringClass();
479         if (!(cd instanceof JavaClass)) {
480             sb.append(jc.getSimpleName());
481             return;
482         }
483         resolveClassName((JavaClass) cd, sb);
484         sb.append('.').append(jc.getSimpleName());
485     }
486
487     public String JavaDoc toString() {
488         return this.pattern;
489     }
490
491     /** Tag for simple types - m,n,t,r,s,c,M */
492     private static class Tag extends Object JavaDoc implements Serializable {
493         /** Tag character */
494         char kind;
495
496         /** Prefix of the tag */
497         String JavaDoc prefix;
498
499         /** Suffix of the tag */
500         String JavaDoc suffix;
501
502         static final long serialVersionUID =4946774706959011193L;
503         /** Creates the tag. */
504         Tag(char kind, String JavaDoc prefix, String JavaDoc suffix) {
505             this.kind = kind;
506             this.prefix = prefix;
507             this.suffix = suffix;
508         }
509
510         /** Formats this tag for the given element.
511         * @param element Element to be formated.
512         * @param buf StringBuffer where to add the formated string.
513         */

514         void format(Element element, StringBuffer JavaDoc buf) {
515             try {
516                 int mark = buf.length();
517                 buf.append(prefix);
518
519                 switch (kind) {
520                 case 'm':
521                     buf.append(Modifier.toString(((ClassMember) element).getModifiers() & ~Modifier.INTERFACE));
522                     break;
523
524                 case 'n':
525                     buf.append(elementName((NamedElement) element));
526                     break;
527
528                 case 'f':
529                     buf.append(elementFullName((NamedElement) element));
530                     break;
531
532                 case 'C':
533                     resolveClassName((JavaClass) element, buf);
534                     break;
535
536                 case 't':
537                     buf.append(((TypedElement) element).getType().getName());
538                     break;
539
540                 case 'r':
541                     buf.append(((TypedElement) element).getType().getName());
542                     break;
543
544                 case 's':
545                     JavaClass superClass = ((JavaClass) element).getSuperClass();
546                     if (superClass != null) {
547                         buf.append(elementFullName(superClass));
548                     }
549                     break;
550
551                 case 'c':
552                     int modifiers = ((Initializer) element).getModifiers();
553                     if (Modifier.isStatic(modifiers))
554                         buf.append(Modifier.toString(Modifier.STATIC));
555                     break;
556                 
557                 case 'M':
558                     buf.append(Modifier.isInterface((((ClassMember) element).getModifiers()))? "interface": "class"); // NOI18N
559
break;
560                 }
561
562                 if (buf.length() > mark + prefix.length()) {
563                     buf.append(suffix);
564                 }
565                 else {
566                     buf.setLength(mark);
567                 }
568             }
569             catch (ClassCastException JavaDoc e) {
570                 throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
571
}
572         }
573     }
574
575     /** Tag for arrays - params, exceptions, interfaces, type parameters.
576     */

577     private static final class ArrayTag extends Tag {
578         /** Delimiter */
579         String JavaDoc delim;
580
581         static final long serialVersionUID =2060398944304753010L;
582         /** Creates new array tag. */
583         ArrayTag(char kind, String JavaDoc prefix, String JavaDoc suffix, String JavaDoc delim) {
584             super(kind, prefix, suffix);
585             this.delim = delim;
586         }
587
588         private void identifiers2String(List/*<MultipartId>*/ l, StringBuffer JavaDoc buf) throws JmiException {
589             Iterator it = l.iterator();
590             boolean toDelimit = false;
591             while (it.hasNext()) {
592                 if (toDelimit) {
593                     buf.append(delim);
594                 } else {
595                     toDelimit = true;
596                 }
597                 MultipartId id = (MultipartId) it.next();
598                 buf.append(IdentifierArrayEditor.multipartIdToName(id));
599             }
600         }
601         
602         /** Formats this tag for the given element.
603         * @param element Element to be formated.
604         * @param buf StringBuffer where to add the formated string.
605         */

606         void format(Element element, StringBuffer JavaDoc buf) {
607             try {
608                 int mark = buf.length();
609                 buf.append(prefix);
610
611                 switch (kind) {
612                 case 'e':
613                     identifiers2String(((CallableFeature) element).getExceptionNames(), buf);
614                     break;
615
616                 case 'p':
617                 case 'a':
618                     if (element instanceof EnumConstant) {
619                         constantParams2String((EnumConstant) element, buf);
620                         break;
621                     }
622                     List l = ((CallableFeature) element).getParameters();
623                     buf.append(
624                             MethodParameterArrayEditor.params2String(
625                                     (Parameter[]) l.toArray(new Parameter[0]), delim, kind == 'p')
626                             );
627                     break;
628
629                 case 'i':
630                     identifiers2String(((JavaClass) element).getInterfaceNames(), buf);
631                     break;
632                 case 'T':
633                     typeParams2String(((GenericElement) element), buf);
634                     break;
635                 }
636
637                 if (buf.length() > mark + prefix.length()) {
638                     buf.append(suffix);
639                 }
640                 else {
641                     buf.setLength(mark);
642                 }
643             } catch (ClassCastException JavaDoc e) {
644                 throw new IllegalArgumentException JavaDoc(NbBundle.getMessage(ElementFormat.class, "MSG_badPattern")); // NOI18N
645
}
646         }
647         
648         private static void constantParams2String(EnumConstant ec, StringBuffer JavaDoc sb) throws JmiException {
649             String JavaDoc value = ec.getInitialValueText();
650             sb.append(value != null? value: ""); // NOI18N
651
}
652         
653         private void typeParams2String(GenericElement ge, StringBuffer JavaDoc sb) throws JmiException {
654             TypeParameter[] tps = (TypeParameter[]) ge.getTypeParameters().toArray(new TypeParameter[0]);
655             String JavaDoc s = TypeParameterArrayEditor.params2String(tps, delim);
656             sb.append(s);
657         }
658     }
659 }
660
Popular Tags