KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > bridge > ElementImpl


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.bridge;
21
22 import java.beans.*;
23 import java.io.IOException JavaDoc;
24 import java.util.*;
25 import java.lang.ref.WeakReference JavaDoc;
26 import org.netbeans.modules.java.JavaEditor;
27
28 import org.openide.nodes.Node;
29 import org.openide.nodes.CookieSet;
30 import org.openide.src.*;
31 import javax.jmi.reflect.RefObject;
32 import javax.jmi.reflect.InvalidObjectException;
33 import org.netbeans.api.mdr.MDRepository;
34 import org.netbeans.api.mdr.events.*;
35 import org.netbeans.jmi.javamodel.Array;
36 import org.netbeans.jmi.javamodel.Feature;
37 import org.netbeans.jmi.javamodel.JavaClass;
38 import org.netbeans.jmi.javamodel.TypeReference;
39 import org.netbeans.jmi.javamodel.ArrayReference;
40 import org.netbeans.jmi.javamodel.JavaModelPackage;
41 import org.netbeans.jmi.javamodel.MultipartId;
42 import org.netbeans.jmi.javamodel.NamedElement;
43 import org.netbeans.jmi.javamodel.ParameterizedType;
44 import org.netbeans.jmi.javamodel.PrimitiveType;
45 import org.netbeans.jmi.javamodel.PrimitiveTypeKind;
46 import org.netbeans.jmi.javamodel.PrimitiveTypeKindEnum;
47 import org.netbeans.jmi.javamodel.TypeParameter;
48 import org.netbeans.jmi.javamodel.UnresolvedClass;
49 import org.netbeans.mdr.handlers.InstanceHandler;
50 import org.netbeans.modules.java.JavaDataObject;
51 import org.netbeans.modules.javacore.ClassIndex;
52 import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
53 import org.netbeans.modules.javacore.jmiimpl.javamodel.TypeClassImpl;
54 import org.openide.ErrorManager;
55 import org.openide.filesystems.FileObject;
56 import org.openide.text.CloneableEditorSupport;
57 import org.openide.util.RequestProcessor;
58 import org.openide.util.Utilities;
59
60 /**
61  *
62  * @author Dan Prusa
63  */

64 public abstract class ElementImpl implements Element.Impl, Element.Impl2, ElementProperties, Node.Cookie, ElementEvents {
65         
66     public static final String JavaDoc JAVA_LANG_OBJECT = "java.lang.Object"; // NOI18N
67

68     protected static final boolean JDK15_CHECKS_DISABLED = "true".equalsIgnoreCase(System.getProperty("org.netbeans.java.bridge.disable_jdk15_checks", "true")); // NOI18N
69

70     /** The element for this class.
71      */

72     private transient Element element;
73
74     /**
75      * Collection of registered PropertyChangeListeners.
76      */

77     private transient Collection propListeners;
78         
79     /** Vetoable change support.
80      * PENDING: Need to create special VetoableChangeSupport, as the standard one
81      * is not sufficient for the fine-grained events.
82      */

83     private transient VetoableChangeSupport vetoSupport;
84
85     /**
86      * Binding to the underlying source/whatever.
87      */

88     protected transient Binding binding;
89     
90     /**
91      * True, if the element has been already created and was not destroyed.
92      */

93     private transient boolean valid;
94
95     /**
96      * Set of cookies for this element. The set conveniently contains only cookies for
97      * the implementation class hierarchy.
98      */

99     private CookieSet cookieSet;
100
101     /**
102      * Model that has created this element and that is used to manage the element.
103      * The element delegates management functions like locking and event queueing to
104      * its model.
105      */

106     protected transient DefaultLangModel model;
107
108     /**
109      * Flag that is used to signal that the element is being created. Some processing
110      * is reduced to minimum during this mode and event firing is supressed.
111      */

112     private transient boolean inCreation;
113     
114     protected JavaDocImpl javadoc;
115     
116     /**
117      * Java element in repository corresponding to this element.
118      */

119     protected transient org.netbeans.jmi.javamodel.Element javaElement;
120     
121     protected transient JavaModelPackage javaModelPackage;
122     
123     protected transient MDRepository repository;
124     
125     private static final Binding NULL_BINDING = new NullBinding();
126     
127     /**
128      * Enables debug trace messages for event generation
129      */

130     private static final boolean DEBUG_EVENTS = false;
131
132     private static final long serialVersionUID = -6337836874152020892L;
133      
134            
135     protected ElementImpl(DefaultLangModel model) {
136         this.model = model;
137         this.javaElement = null;
138         this.inCreation = false;
139         this.valid = true;
140         this.repository = JavaMetamodel.getDefaultRepository ();
141         binding = NULL_BINDING;
142     }
143     
144     protected ElementImpl(DefaultLangModel model, RefObject javaElement) {
145         this.model = model;
146         this.javaElement = (org.netbeans.jmi.javamodel.Element)javaElement;
147         this.inCreation = false;
148         this.valid = true;
149         this.repository = JavaMetamodel.getDefaultRepository ();
150         binding = NULL_BINDING;
151         repository.beginTrans(false);
152         try {
153             if (this.javaElement.isValid())
154                 javaModelPackage = (JavaModelPackage) javaElement.refImmediatePackage ();
155         } finally {
156             repository.endTrans();
157         }
158     }
159     
160     public RefObject getJavaElement () {
161         return javaElement;
162     }
163     
164     public Type descrToType (org.netbeans.jmi.javamodel.Type type) {
165         return descrToType (TypeClassImpl.getRawType(type), null);
166     }
167     
168     public Type descrToType (org.netbeans.jmi.javamodel.Type descr, String JavaDoc sourceName) {
169         if (descr == null)
170             return null;
171         if (descr instanceof TypeParameter) {
172             return Type.createClass (createClassIdentifier (JAVA_LANG_OBJECT));
173         }
174         if (descr instanceof ParameterizedType) {
175             if (((ParameterizedType)descr).getParameters().size() == 0) {
176                 descr = ((ParameterizedType)descr).getDefinition();
177             } else {
178                 if (sourceName == null)
179                     return Type.createClass (createClassIdentifier (JAVA_LANG_OBJECT));
180                 else
181                     return Type.createClass (createClassIdentifier (JAVA_LANG_OBJECT, sourceName));
182             }
183         }
184         if (descr instanceof Array) {
185             return Type.createArray (descrToType (((Array) descr).getType ()));
186         } else if (descr instanceof JavaClass) {
187             // [MaM] migration
188
String JavaDoc name = ((JavaClass) descr).getName ();
189 // if (name.equals ("String")) {
190
// name = "java.lang.String";
191
// }
192
// System.out.println("descrToType, CLASS: " + name);
193
if (sourceName == null)
194                 return Type.createClass (createClassIdentifier (name));
195             else
196                 return Type.createClass (createClassIdentifier (name, sourceName));
197         } else {
198             // primitive type
199
PrimitiveTypeKind kind = ((PrimitiveType) descr).getKind ();
200             if (PrimitiveTypeKindEnum.BOOLEAN.equals (kind))
201                 return Type.BOOLEAN;
202             if (PrimitiveTypeKindEnum.INT.equals (kind))
203                 return Type.INT;
204             if (PrimitiveTypeKindEnum.CHAR.equals (kind))
205                 return Type.CHAR;
206             if (PrimitiveTypeKindEnum.BYTE.equals (kind))
207                 return Type.BYTE;
208             if (PrimitiveTypeKindEnum.SHORT.equals (kind))
209                 return Type.SHORT;
210             if (PrimitiveTypeKindEnum.LONG.equals (kind))
211                 return Type.LONG;
212             if (PrimitiveTypeKindEnum.FLOAT.equals (kind))
213                 return Type.FLOAT;
214             if (PrimitiveTypeKindEnum.DOUBLE.equals (kind))
215                 return Type.DOUBLE;
216             return descr != null ? Type.VOID : null; // [PENDING] should null be returned ???
217
}
218     }
219     
220     // [MaM] this method should probably by changed to "resolveType"
221
public org.netbeans.jmi.javamodel.Type typeToDescr (Type type) {
222         if (type.isArray ()) {
223             org.netbeans.jmi.javamodel.Type jmiType = typeToDescr(type.getElementType());
224             JavaModelPackage pck = ((JavaModelPackage)jmiType.refImmediatePackage());
225             return pck.getArray().resolveArray(jmiType);
226         } else {
227             if (type.isClass()) {
228                 return getClassDescriptor(type.getClassName().getFullName());
229             } else {
230                 // primitive type
231
return (PrimitiveType) javaModelPackage.getType().resolve(type.getFullString());
232                 
233                 // [MaM] migration to the new infrastructure
234
// PrimitiveTypeKind tag;
235
// if (Type.BOOLEAN.equals (type))
236
// tag = PrimitiveTypeKindEnum.BOOLEAN;
237
// else if (Type.INT.equals (type))
238
// tag = PrimitiveTypeKindEnum.INT;
239
// else if (Type.CHAR.equals (type))
240
// tag = PrimitiveTypeKindEnum.CHAR;
241
// else if (Type.BYTE.equals (type))
242
// tag = PrimitiveTypeKindEnum.BYTE;
243
// else if (Type.SHORT.equals (type))
244
// tag = PrimitiveTypeKindEnum.SHORT;
245
// else if (Type.LONG.equals (type))
246
// tag = PrimitiveTypeKindEnum.LONG;
247
// else if (Type.FLOAT.equals (type))
248
// tag = PrimitiveTypeKindEnum.FLOAT;
249
// else if (Type.DOUBLE.equals (type))
250
// tag = PrimitiveTypeKindEnum.DOUBLE;
251
// else
252
// tag = PrimitiveTypeKindEnum.VOID;
253
// return codeBase.createPrimitive(tag);
254
}
255         }
256     }
257     
258     public TypeReference typeToTypeReference(Type type) {
259         if (type.isArray ()) {
260             int dimCount = 0;
261             do {
262                 dimCount++;
263                 type = type.getElementType();
264             } while (type.isArray());
265             String JavaDoc name = type.getSourceString();
266             if (name == null)
267                 name = type.getFullString();
268             MultipartId parent = javaModelPackage.getMultipartId().createMultipartId(name, null, null);
269             return javaModelPackage.getArrayReference().createArrayReference("", parent, dimCount);
270         } else {
271             String JavaDoc name = type.getSourceString();
272             if (name == null)
273                 name = type.getFullString();
274             return javaModelPackage.getMultipartId().createMultipartId(name, null, null);
275         }
276     }
277     
278     public Type stringToType (String JavaDoc name) {
279         return stringToType(name, name);
280     }
281     
282     public Type stringToType (String JavaDoc fullName, String JavaDoc sourceName) {
283         if ("boolean".equals(fullName)) // NOI18N
284
return Type.BOOLEAN;
285         if ("int".equals (fullName)) // NOI18N
286
return Type.INT;
287         if ("char".equals (fullName)) // NOI18N
288
return Type.CHAR;
289         if ("byte".equals (fullName)) // NOI18N
290
return Type.BYTE;
291         if ("short".equals (fullName)) // NOI18N
292
return Type.SHORT;
293         if ("long".equals (fullName)) // NOI18N
294
return Type.LONG;
295         if ("float".equals (fullName)) // NOI18N
296
return Type.FLOAT;
297         if ("double".equals (fullName)) // NOI18N
298
return Type.DOUBLE;
299         if ("void".equals (fullName) || fullName == null) // [PENDING] // NOI18N
300
return Type.VOID;
301         return Type.createClass (createClassIdentifier(fullName, sourceName));
302     }
303     
304     public Type typeReferenceToType (TypeReference descr) {
305         if (descr == null)
306             return null;
307         Type type = null;
308         if (descr instanceof ArrayReference) {
309             type = multipartIdToType(descr.getParent());
310             int dimCount = ((ArrayReference) descr).getDimCount();
311             for (int x = 0; x < dimCount; x++) {
312                 type = Type.createArray (type);
313             }
314         } else {
315             type = multipartIdToType((MultipartId)descr);
316         }
317         return type;
318     }
319     
320     private Type multipartIdToType(MultipartId id) {
321         String JavaDoc sourceName = multipartIdToName(id);
322         String JavaDoc fullName;
323         NamedElement elem = id.getElement();
324         int resolved = Identifier.RESOLVED;
325         if (elem instanceof PrimitiveType)
326             return stringToType(sourceName, null);
327         if (JAVA_LANG_OBJECT.equals(sourceName)) {
328             fullName = sourceName;
329         } else {
330             if (elem != null && (elem instanceof JavaClass)) {
331                 fullName = ((JavaClass) elem).getName();
332             } else {
333                 fullName = null;
334             }
335             
336             // for some reason the fullname is sometimes null even if the
337
// above condition is true (a class returns null from get name?)
338
if (fullName == null) {
339                 fullName = sourceName;
340                 resolved = Identifier.UNRESOLVED;
341             }
342         }
343         return Type.createClass(Identifier.create(fullName, sourceName, resolved));
344     }
345     
346     // [MaM] - migration to the new infrastructure
347
// this method should probably by changed to "resolveClass"
348
public org.netbeans.jmi.javamodel.Type getClassDescriptor (String JavaDoc fullName) {
349         return javaModelPackage.getType().resolve(fullName);
350 // ClassDescriptor res = getCodebase ().findClassDescriptor(fullName, true);
351
// return res;
352
}
353
354     public Identifier createClassIdentifier (MultipartId id) {
355         String JavaDoc sourceName = multipartIdToName(id);
356         String JavaDoc fullName = null;
357         JavaClass jcls = (JavaClass)id.getElement();
358         if (JAVA_LANG_OBJECT.equals(sourceName)) {
359             fullName = sourceName;
360         } else {
361             fullName = jcls == null ? sourceName : typeToFullName(jcls);
362         }
363         int resolved = Identifier.RESOLVED;
364         
365         if (jcls == null || jcls instanceof UnresolvedClass)
366             resolved = Identifier.UNRESOLVED;
367         
368         return Identifier.create(fullName, sourceName, resolved);
369     }
370     
371     public static String JavaDoc multipartIdToName(MultipartId id) {
372         LinkedList list = new LinkedList();
373         while (id != null) {
374             
375             if (!id.getTypeArguments().isEmpty()) {
376                 return JAVA_LANG_OBJECT;
377             }
378             
379             list.addFirst(id.getName());
380             id = id.getParent();
381         }
382         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
383         for (Iterator iter = list.iterator(); iter.hasNext();) {
384             buf.append((String JavaDoc)iter.next());
385             if (iter.hasNext())
386                 buf.append('.');
387         }
388         return buf.toString();
389     }
390     
391     public String JavaDoc typeToFullName(JavaClass jc) {
392         if (jc instanceof ParameterizedType) {
393             ParameterizedType p = (ParameterizedType) jc;
394             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
395             buf.append(typeToFullName(p.getDefinition()));
396             List pars = p.getParameters();
397             if (pars.size() > 0) {
398                 buf.append('<');
399                 for (Iterator iter = pars.iterator(); iter.hasNext(); ) {
400                     JavaClass par = (JavaClass) iter.next();
401                     buf.append(typeToFullName(par));
402                     if (iter.hasNext())
403                         buf.append(',');
404                 }
405                 buf.append('>');
406             }
407             return buf.toString();
408         } else {
409             return jc.getName();
410         }
411     }
412     
413     public Identifier createClassIdentifier (String JavaDoc fullName) {
414         return createClassIdentifier (fullName, fullName);
415     }
416     
417     public Identifier createClassIdentifier (String JavaDoc fullName, String JavaDoc sourceName) {
418 // ProjectModel pm = ProjectModel.getDefault ();
419

420         // [MaM] migration to the new infrastructure
421
repository.beginTrans(false);
422         try {
423             setClassPath();
424             boolean exists = ClassIndex.hasClass(fullName, JavaMetamodel.getManager().getClassPath());
425             //boolean exists = pm.getDefaultClassPath ().exists (fullName);
426
return Identifier.create (
427                 fullName, sourceName,
428                 exists ? Identifier.RESOLVED : Identifier.UNRESOLVED
429             );
430         } finally {
431             repository.endTrans(false);
432         }
433     }
434     
435     public void checkIsValid () throws SourceException {
436         if (!javaElement.isValid()) {
437             throwIsInvalid ();
438         }
439     }
440     
441     public void throwIsInvalid () throws SourceException {
442         Util.throwException("Element was deleted", "EXC_ElementInvalid"); // NOI18N
443
}
444     
445     public abstract void connectListener ();
446     
447     /**
448      * Attaches the abstract layer to the element; since the all properties required
449      * for the Binding to operate are available, the binding is created as well.
450      */

451     public void attachedToElement(Element el) {
452         this.element = el;
453         if (!valid)
454             return;
455         repository.beginTrans (false);
456         try {
457             if (!javaElement.isValid()) {
458                 setValid(false);
459             } else {
460                 connectListener ();
461                 setValid (true);
462             }
463         } finally {
464             repository.endTrans (false);
465         }
466     }
467     
468     /**
469      * Binds the element to a particular underlying Binding. The function does nothing
470      * if the binding was already establised; it is impossible to rebind an element once
471      * the binding was established (this constraint may be overriden by descendants)
472      *
473      * @param b binding to use for element i/o operations.
474      */

475     public void setBinding(Binding b) {
476         if (this.binding != null)
477             return;
478         if (b instanceof Node.Cookie) {
479             getCookieSet().add((Node.Cookie)b);
480         }
481         binding = b;
482     }
483     
484     // Listener interface functions
485
///////////////////////////////////////////////////////////////////////////////////
486

487     private String JavaDoc describeEvent(PropertyChangeEvent evt) {
488         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
489         sb.append(evt.getPropertyName());
490         Object JavaDoc oldV = evt.getOldValue();
491         sb.append(" old = " + describeValue(evt.getOldValue())); // NOI18N
492
sb.append(" new = " + describeValue(evt.getNewValue())); // NOI18N
493
return sb.toString();
494     }
495     
496     private String JavaDoc describeValue(Object JavaDoc o) {
497         if (o instanceof Identifier) {
498             Identifier id = (Identifier)o;
499             return id.getSourceName() + "/" + id.getFullName() + "/" + // NOI18N
500
id.getResolutionStatus();
501         } else if (o instanceof Identifier[]) {
502             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
503             sb.append("[ "); // NOI18N
504
Identifier[] ids = (Identifier[])o;
505             for (int i = 0; i < ids.length; i++) {
506                 if (i > 0)
507                     sb.append(", "); // NOI18N
508
sb.append(describeValue(ids[i]));
509             }
510             sb.append(" ]"); // NOI18N
511
return sb.toString();
512         } else if (o instanceof MethodParameter[]) {
513             MethodParameter[] pars = (MethodParameter[])o;
514             if (pars.length == 0)
515                 return "()"; // NOI18N
516
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
517             for (int i = 0; i < pars.length; i++) {
518                 if (i > 0)
519                     sb.append(", "); // NOI18N
520
sb.append(pars[i].getSourceString());
521                 sb.append("/"); // NOI18N
522
Type t = pars[i].getType();
523                 sb.append(t.getFullString());
524                 while (t.isArray())
525                     t = t.getElementType();
526                 if (t.isClass()) {
527                     sb.append('/');
528                     sb.append(new Integer JavaDoc(t.getTypeIdentifier().getResolutionStatus()).toString());
529                 }
530             }
531             return sb.toString();
532         } else if (o instanceof Type) {
533             Type t = (Type)o;
534             String JavaDoc s = t.getSourceString() + "/" + t.getFullString(); // NOI18N
535
while (t.isArray())
536                 t = t.getElementType();
537             if (t.isClass()) {
538                 s = s + "/" + t.getTypeIdentifier().getResolutionStatus(); // NOI18N
539
}
540             return s;
541         } else if (o != null) {
542             return o.toString();
543         } else return "null"; // NOI18N
544
}
545
546     /** Adds property listener.
547      */

548     public void addPropertyChangeListener (PropertyChangeListener l) {
549         if (DEBUG_EVENTS) {
550             System.err.println("[" + this + "] attaching listener " + l); // NOI18N
551
}
552         if (propListeners == null) {
553             synchronized (this) {
554                 // new test under synchronized block
555
if (propListeners == null) {
556                     propListeners = new LinkedList();
557                     initializeListenerSupport();
558                 }
559             }
560         }
561     synchronized (propListeners) {
562         propListeners.add(l);
563     }
564     }
565
566     /**
567      * Returns true, if the element is fully created. This yes/no test is used for supression
568      * of some event firing and lightweight operations.
569      */

570     protected boolean isCreated() {
571         return this.inCreation;
572     }
573     
574     /** Removes property listener */
575     public void removePropertyChangeListener (PropertyChangeListener l) {
576         if (DEBUG_EVENTS) {
577             System.err.println("[" + this + "] removing listener " + l); // NOI18N
578
}
579     if (propListeners != null) {
580         synchronized (propListeners) {
581         propListeners.remove(l);
582         }
583     }
584     }
585
586     /** Adds property vetoable listener */
587     public void addVetoableChangeListener (VetoableChangeListener l) {
588         if (DEBUG_EVENTS) {
589             System.err.println("[" + this + "] attaching veto listener " + l); // NOI18N
590
}
591         if (vetoSupport == null) {
592             synchronized (this) {
593                 // new test under synchronized block
594
if (vetoSupport == null) {
595                     vetoSupport = new VetoableChangeSupport (element);
596                     initializeListenerSupport();
597                 }
598             }
599         }
600         vetoSupport.addVetoableChangeListener (l);
601     }
602     
603     /** Removes property vetoable listener */
604     public void removeVetoableChangeListener (VetoableChangeListener l) {
605         if (DEBUG_EVENTS) {
606             System.err.println("[" + this + "] removing veto listener " + l); // NOI18N
607
}
608         if (vetoSupport != null) {
609             vetoSupport.removeVetoableChangeListener (l);
610         }
611     }
612
613     /** true if some vetoable listener is currently registered */
614     public boolean hasVetoableListeners (String JavaDoc propName) {
615         if (vetoSupport == null)
616             return false;
617         else
618             return vetoSupport.hasListeners (propName);
619     }
620     
621     /** Base method for marking the current insertion point.
622      */

623     public void markCurrent(boolean beforeAfter) {
624         // PENDING: redirect to the new facilities for element ordering.
625
}
626     
627     /** Returns the abstract wrapper for this implementation.
628      */

629     public final Element getElement() {
630         return element;
631     }
632     
633     /**
634      * Returns the cookie set so that extension objects can plug in and
635      * extend the element implementation.
636      * @return r/w cookie set instance.
637      */

638     public final CookieSet getCookieSet() {
639         if (cookieSet == null) {
640             synchronized (this) {
641                 if (cookieSet == null) {
642                     cookieSet = new CookieSet();
643                     initializeCookies(cookieSet);
644                 }
645             }
646         }
647         return cookieSet;
648     }
649     
650     protected void initializeCookies(CookieSet set) {
651         set.add(this);
652     }
653     
654     /**
655      * Returns true, if the element is still valid - that is present in the model.
656      * @return true, iff the element is still valid.
657      */

658     public boolean isValid() {
659         return this.valid;
660     }
661     
662     // Support functions
663
/////////////////////////////////////////////////////////////////////////////////
664

665     /** Fires property change event. The event is fired as "own" property change event,
666      * so it is captured in the queue and counted for the summary information.
667     * @param name property name
668     * @param o old value
669     * @param n new value
670     */

671     protected final void firePropertyChange(String JavaDoc name, Object JavaDoc o, Object JavaDoc n) {
672         fireOwnPropertyChange(new PropertyChangeEvent(getElement(), name, o, n));
673     }
674     
675     /**
676      * Fires a PropertyChangeEvent to the listeners, if there's a listener.
677      */

678     protected final void firePropertyChangeEvent(PropertyChangeEvent evt) {
679         if (DEBUG_EVENTS) {
680             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
681             sb.append('[');
682             sb.append(toString());
683             sb.append("] Dispatching change: "); // NOI18N
684
sb.append(describeEvent(evt));
685             System.err.println(sb.toString());
686         }
687     if (propListeners == null)
688         return;
689     
690     Vector listeners;
691     synchronized (propListeners) {
692         listeners = new Vector(propListeners);
693     }
694     for (int i = 0; i < listeners.size(); i++) {
695         PropertyChangeListener l = (PropertyChangeListener)listeners.elementAt(i);
696         l.propertyChange(evt);
697     }
698     }
699     
700     /**
701      * Fires an arbitrary property change event about intrinsic property change.
702      * Events fired though this method will show up in the change summary events
703      * fired after the model's lock is released. The method does nothing if the element
704      * is in the creation phase.<P>
705      * <B>Important note:<B> since the implementation will record the current element's
706      * state by creating a copy/clone, it is <B>required</B> that the change is fired
707      * before the element updates its internal state so that the clone contains the
708      * old one.
709      */

710     protected final void fireOwnPropertyChange(PropertyChangeEvent evt) {
711         if (evt==null)
712             return;
713
714         /*
715         final int MAX = 20;
716         String oldValue = evt.getOldValue () != null ? evt.getOldValue ().toString () : "";
717         if (oldValue.length () > MAX)
718             oldValue = oldValue.substring (0, MAX);
719         String newValue = evt.getNewValue () != null ? evt.getNewValue ().toString () : "";
720         if (newValue.length () > MAX)
721             newValue = newValue.substring (0, MAX);
722          */

723         
724         // System.out.println("EVT: " + evt.getPropertyName() + " " + oldValue + " " + newValue + " " + this.hashCode());
725

726         firePropertyChangeEvent (evt);
727         /*
728         if (isCreated())
729             return;
730         EventQueue q = getModelImpl().getEventQueue();
731         q.elementChanged(this);
732         addPropertyChange(evt);
733          */

734     }
735     
736     protected Element cloneSelf() {
737         throw new UnsupportedOperationException JavaDoc("clone unsupported on " // NOI18N
738
+ getClass());
739     }
740
741     /**
742      * Adds an extrinsic property change event to the final event queue.
743      * Events channeled through this method do not contribute to the summary events,
744      * for example properties that contain children sub-elements should use
745      * this method to avoid unnecessary element cloning.
746      */

747     public final void addPropertyChange(PropertyChangeEvent evt) {
748         if (isCreated())
749             return;
750         getModelImpl().getEventQueue().addPropertyChange(this, evt);
751     }
752     
753     /** Fires a vetoable change on property `name' from old value `o' to new value `n'.
754      * @param name name of the property that is being changed.
755      * @param o old value of the property; can be null.
756      * @param n new value of the property; can be null.
757      */

758     protected final void fireVetoableChange(String JavaDoc name, Object JavaDoc o, Object JavaDoc n) throws PropertyVetoException {
759         if (isCreated())
760             return;
761         if (vetoSupport != null) {
762             try {
763                 getModelImpl().notifyEventsDispatched(true);
764                 vetoSupport.fireVetoableChange(name, o, n);
765             } finally {
766                 getModelImpl().notifyEventsDispatched(false);
767             }
768         }
769     }
770     
771     /**
772      * Fires arbitrary pre-constructed vetoable change event.
773      * @param evt vetoable event that should be fired.
774      */

775     public final void fireVetoableChange(PropertyChangeEvent evt) throws SourceException {
776         if (isCreated())
777             return;
778          checkVetoablePropertyChange(evt);
779     }
780     
781     private void doFireVetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
782         if (DEBUG_EVENTS) {
783             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
784             sb.append('[');
785             sb.append(toString());
786             sb.append("] Dispatching veto: "); // NOI18N
787
sb.append(describeEvent(evt));
788             System.err.println(sb.toString());
789         }
790         if (vetoSupport != null) {
791             try {
792                 getModelImpl().notifyEventsDispatched(true);
793                 vetoSupport.fireVetoableChange(evt);
794             } finally {
795                 getModelImpl().notifyEventsDispatched(false);
796             }
797         }
798     }
799     
800     /** The method tries to fire a Vetoable Change event; if (one of) listener throws
801      * PropertyVetoException, the exception is examined whether it holds a wrapped SourceException.
802      * If so, that inner SourceException is rethrown, otherwise, the PropertyVetoException is
803      * wrapped into a SourceException and thrown to the caller.
804      */

805     protected void checkVetoablePropertyChange(PropertyChangeEvent evt) throws SourceException {
806         if (isCreated() || !isConstrained())
807             return;
808         
809         try {
810             doFireVetoableChange(evt);
811         } catch (SourceVetoException ex) {
812             // rethrow the original exception.
813
throw ex.getNestedException();
814         } catch (PropertyVetoException ex) {
815             // rethrow the veto as a general SourceException.
816
// PENDING: use model's environment to annotate/log the exception(s).
817
throw new SourceException(ex.getMessage());
818         }
819     }
820     
821     /**
822      * Returns the storage binding for this element. The element must be
823      * backed up by a text file/document, but the changes themselves and the binding
824      * to the underlying document are performed by other delegate object.
825      * If the element is just being created, the function returns NULL_BINDING that accepts
826      * all requests, but does nothing. This way the element is blocked from altering the
827      * storage until it is fully created.
828      *
829      * @return The binding for this element, or, if the element has inCreation flag,
830      * it returns the default NULL_BINDING.
831      */

832     public final Binding getBinding() {
833         if (isCreated())
834             return NULL_BINDING;
835         return this.binding;
836     }
837
838     /**
839      * Returns the actual binding without any shielding for elements that are not yet
840      * created.
841      */

842     public final Binding getRawBinding() {
843         return getBinding ();
844     }
845     
846     /** Invalidates the element. The element will be no longer valid and may refuse some
847      * operations that would depend on other objects in the model - since it will be no
848      * longer considered to be a part of a model.
849      */

850     protected void invalidate() {
851         setValid(false);
852     }
853     
854     private void setValid(boolean valid) {
855         boolean old = this.valid;
856         if (old == valid)
857             return;
858         this.valid = valid;
859         if (old)
860             // do not clone the element upon THIS property change.
861
fireOwnPropertyChange(new PropertyChangeEvent(getEventSource(),
862                 PROP_VALID,
863                 valid ? Boolean.FALSE : Boolean.TRUE,
864                 valid ? Boolean.TRUE : Boolean.FALSE
865             ));
866     }
867
868     /**
869      * Checks whether the element is still valid. If not, throws a SourceException
870      * to indicate that the calling operation cannot be completed.
871      * @throws SourceException if the element is not valid/present in the model.
872      */

873     protected void checkValid(Object JavaDoc lockToken) throws SourceException {
874         if (isValid()) {
875             return;
876         }
877         releaseLock(lockToken);
878         Util.throwException("Element was deleted", "EXC_ElementInvalid"); // NOI18N
879
}
880
881     public void checkDocument() {
882         JavaDataObject jdo = model.getJavaDataObject();
883         CloneableEditorSupport edSupport = (CloneableEditorSupport) jdo.getCookie (JavaEditor.class);
884         if (!edSupport.isDocumentLoaded()) {
885             try {
886                 edSupport.openDocument();
887             } catch (IOException JavaDoc e) {
888                 ErrorManager.getDefault().notify(e);
889             }
890         }
891     }
892
893     public void setClassPath() {
894         JavaDataObject jdo = model.getJavaDataObject();
895         FileObject fo = jdo.getPrimaryFile();
896         JavaMetamodel.getManager().setClassPath(fo);
897     }
898     
899     /**
900      * JDK 1.5 - checks if the element is not read only
901      */

902     protected abstract void checkWritable(boolean unsafeOp) throws SourceException;
903     
904     /** Retrieves cookie supported by the Element. In general, implementation
905      * classes are ALWAYS available as cookies, so they can be extracted from both ElementImpls
906      * and the abstract counterparts.
907      */

908     public Node.Cookie getCookie(Class JavaDoc desired) {
909         // return this instance, if the cookie is directly supported.
910
if (desired.isAssignableFrom(getClass()))
911             return this;
912         // ask the CookieSet
913
Node.Cookie ret = getCookieSet().getCookie(desired);
914         if (ret != null)
915             return ret;
916         return getModelImpl().findElementCookie(getElement(), desired);
917     }
918     
919     
920     /** Finds the source element - the root for the hierarchy this element are part of.
921      */

922     protected abstract SourceElementImpl findSource();
923     
924     // Package-private protocol
925
////////////////////////////////////////////////////////////////////////
926
protected void createAfter(Binding.Container cb, Binding refBinding)
927     throws SourceException {
928         inCreation = false;
929         cb.insert(binding, refBinding);
930         setValid(true);
931     }
932     
933     protected abstract boolean parentValid();
934     
935     public void notifyConnectionChange (Element old) {
936         getModelImpl().fireElementChanged (old, getElement ());
937     }
938     
939     public void notifyConnectionAdd (Element newElem) {
940         getModelImpl().fireElementAdded (newElem);
941     }
942     
943     public void notifyConnectionRemove (Element removedElem) {
944         getModelImpl().fireElementRemoved (removedElem);
945     }
946     
947     public void notifyConnectionSet (Element oldElem, Element newElem) {
948         getModelImpl().fireElementSet (oldElem, newElem);
949     }
950     
951     protected void notifyCreate() {
952         inCreation = false;
953         setValid(true);
954         if (parentValid())
955             notifyElementCreated();
956     }
957     
958     protected void notifyElementCreated() {
959         getModelImpl().getEventQueue().elementCreated(getElement());
960     }
961     
962     protected void setJavaDocText(String JavaDoc content, boolean raw) throws SourceException {
963         if (!(javaElement instanceof Feature)) {
964             return;
965         }
966         
967         checkWritable(false);
968         checkDocument();
969         boolean failed = true;
970         repository.beginTrans (true);
971         try {
972             if (javaElement.isValid()) {
973                 setClassPath();
974                 String JavaDoc oldContent = raw ? javadoc.getRawText() : javadoc.getText();
975                 if (content == oldContent || (content != null && oldContent != null &&
976                     content.equals(oldContent))) {
977                     failed = false;
978                     return;
979                 }
980                 javadoc.changeJavaDocText(content, raw);
981                 ((Feature) javaElement).setJavadocText(javadoc.getRawText ());
982                 failed = false;
983             } else {
984                 failed = false;
985                 throwIsInvalid ();
986             }
987         } finally {
988             repository.endTrans (failed);
989         }
990     }
991     
992     protected void changeJavaDocTags(JavaDocTag[] tags, int action)
993         throws SourceException {
994         
995         if (!(javaElement instanceof Feature)) {
996             return;
997         }
998     
999         checkWritable(false);
1000        checkDocument();
1001        boolean failed = true;
1002        repository.beginTrans (true);
1003        try {
1004            if (javaElement.isValid()) {
1005                setClassPath();
1006                String JavaDoc oldContent = javadoc.getRawText();
1007                javadoc.changeJavaDocTags(tags, action);
1008                ((Feature) javaElement).setJavadocText(javadoc.getRawText ());
1009                failed = false;
1010            } else {
1011                failed = false;
1012                throwIsInvalid ();
1013            }
1014        } finally {
1015            repository.endTrans (failed);
1016        }
1017    }
1018
1019    public void fireJavaDocChange () {
1020        fireOwnPropertyChange (new PropertyChangeEvent(getEventSource(), PROP_JAVADOC, null, null));
1021    }
1022    
1023    /**
1024     * Notifies the element that it has been removed.
1025     */

1026    protected void notifyRemove() {
1027        invalidate();
1028        getModelImpl().getEventQueue().elementRemoved(getElement());
1029    }
1030    
1031    /**
1032     * Checks whether the element can be removed. Fires a Vetoable property change
1033     * event on PROP_VALID from true to false.
1034     */

1035    protected void checkRemove() throws SourceException {
1036        if (isCreated() || !isConstrained())
1037            return;
1038        PropertyChangeEvent evt = new PropertyChangeEvent(getElement(),
1039            PROP_VALID, Boolean.TRUE, Boolean.FALSE);
1040        checkVetoablePropertyChange(evt);
1041    }
1042    
1043    /** Determines whether there's somebody interested in the property. Subclasses may
1044     * then optimize whether they should ever generate PropertyChangeEvent.
1045     */

1046    protected boolean hasListeners(String JavaDoc propName) {
1047    if (vetoSupport.hasListeners(propName))
1048        return true;
1049    if (propListeners == null)
1050        return false;
1051    synchronized (propListeners) {
1052        return !propListeners.isEmpty();
1053    }
1054    }
1055    
1056    /**
1057     * Attempts to run atommically an operation. Throws SourceException if the runnable
1058     * throws any kind of exception
1059     * @deprecated use {@link #takeLock}/{@link #releaseLock} instead.
1060     */

1061    protected void runAtomic(ExceptionRunnable r) throws SourceException {
1062        model.runAtomic(r);
1063    }
1064    
1065    protected void initializeListenerSupport() {
1066    }
1067
1068    /**
1069     * Implementation of {@link ElementEvents} interface; returns the element to
1070     * be reported as the source of events.
1071     */

1072    public final Object JavaDoc getEventSource() {
1073        return getElement();
1074    }
1075    
1076    /**
1077     * Implementation of {@link ElementEvents} interface; returns the implementation
1078     * object paired with the event source (this object).
1079     */

1080    public final ElementImpl getElementImpl() {
1081        return this;
1082    }
1083    
1084    protected abstract void setParent(ElementImpl parent);
1085    
1086    protected void setParent(Element parent) {
1087        setParent((ElementImpl)parent.getCookie(ElementImpl.class));
1088    }
1089    
1090    /**
1091     * Convenience method that retrieves reference to the model that had created
1092     * the element.
1093     */

1094    protected final DefaultLangModel getModelImpl() {
1095        return this.model;
1096    }
1097    
1098    /**
1099     * Attempts to obtain model's write lock preventing other threads from modifying
1100     * the model. Before it returns, the method also checks whether the element
1101     * is still valid. If not, it releases the lock and throws a SourceException
1102     * @return token object that should be later used to free the lock.
1103     */

1104    protected final Object JavaDoc takeLock() {
1105        if (isCreated())
1106            return null;
1107        Object JavaDoc o = getModelImpl().writeLock();
1108        return o;
1109    }
1110
1111    /**
1112     * Releases write lock on the model. If an invalid token object is passed,
1113     * the model will throw IllegalArgumentException
1114     * @param o token object for lock release operation.
1115     */

1116    protected final void releaseLock(Object JavaDoc o){
1117        if (isCreated())
1118            return;
1119        getModelImpl().releaseWriteLock(o);
1120    }
1121
1122    protected final void takeReadLock() {
1123        getModelImpl().readLock();
1124    }
1125
1126    protected final void releaseReadLock() {
1127        getModelImpl().releaseReadLock();
1128    }
1129    
1130    /**
1131     * Takes a <B>master</B> lock. This lock differs from the ordinary in that it
1132     * does not create nested event queue, but merges all events to the current one.
1133     * Until the master lock is in effect, all events will be routed to the current
1134     * event queue. The master lock is used when the model's implementation is about
1135     * to issue nested model operations.
1136     */

1137    protected final Object JavaDoc takeMasterLock() throws SourceException {
1138        if (isCreated())
1139            return null;
1140        Object JavaDoc l = getModelImpl().masterWriteLock();
1141        checkValid(l);
1142        return l;
1143    }
1144    
1145    protected abstract void createFromModel(Element model) throws SourceException;
1146    
1147    /**
1148     * Causes changes made by the last locked operation to be confirmed and,
1149     * upon lock release, merged into the higher operation.
1150     */

1151    protected final void commit() {
1152        if (!isValid())
1153            return;
1154        getModelImpl().commitChanges();
1155    }
1156    
1157    /**
1158     * Returns true, if constraints on elements should be checked. This can
1159     * be disabled during some special operations (like external model updates).
1160     * If the method returns false, no VetoableChangeListeners should be informed.
1161     * @return true, if constraints are enabled, false otherwise.
1162     */

1163    protected final boolean isConstrained() {
1164        return getModelImpl().isConstrained();
1165    }
1166    
1167    protected Identifier createLocalIdentifier(Identifier id, int status) {
1168        if (id.getResolutionStatus() == status)
1169            return id;
1170        return Identifier.create(id.getFullName(), id.getSourceName(), status);
1171    }
1172    
1173    protected boolean checkIdentifierContext(Identifier id) {
1174        return false;
1175    }
1176   
1177    // ..........................................................................
1178

1179    static abstract class ElementListener implements MDRChangeListener {
1180        
1181        public static boolean REGISTER_LISTENER = true;
1182        
1183        protected RefObject javaElement;
1184        protected LWeakReference ref;
1185        protected ElementImpl impl;
1186        protected Object JavaDoc source;
1187        
1188        public boolean isValid = true;
1189        
1190        ElementListener (ElementImpl impl) {
1191            javaElement = impl.getJavaElement ();
1192            ref = new LWeakReference (impl, this);
1193        }
1194        
1195        public ElementImpl getImpl () {
1196            return (ElementImpl) ref.get();
1197        }
1198        
1199        public RefObject getJavaElement () {
1200            return javaElement;
1201        }
1202        
1203        public void connect () {
1204            if (REGISTER_LISTENER) {
1205                for (Iterator iter = ((InstanceHandler) javaElement).getListeners().iterator(); iter.hasNext(); ) {
1206                    Object JavaDoc listener = iter.next();
1207                    if (listener instanceof ElementListener) {
1208                        ((ElementListener) listener).checkValidity();
1209                    }
1210                }
1211                ((MDRChangeSource) javaElement).addListener (this);
1212                isValid = true;
1213            }
1214        }
1215        
1216        public void remove () {
1217            try {
1218                ((MDRChangeSource) javaElement).removeListener (this);
1219            } catch (InvalidObjectException e) {
1220            }
1221            isValid = false;
1222        }
1223
1224        void doRemove() {
1225            MDRepository repository = JavaMetamodel.getDefaultRepository();
1226            repository.beginTrans(false);
1227            try {
1228                remove ();
1229            } finally {
1230                repository.endTrans(false);
1231            }
1232        }
1233        
1234        public void checkValidity() {
1235            if (ref.get() == null) {
1236                doRemove ();
1237            }
1238        }
1239        
1240        public final void change(MDRChangeEvent event) {
1241            if (!isValid)
1242                return;
1243            
1244            try {
1245                impl = (ElementImpl) ref.get();
1246                if (impl == null) {
1247                    doRemove();
1248                    return;
1249                }
1250                source = event.getSource ();
1251                
1252                if (source == javaElement) {
1253                    if (event.isOfType (InstanceEvent.EVENT_INSTANCE_DELETE)) {
1254                        doRemove ();
1255                        impl.setValid (false);
1256                        return;
1257                    }
1258                }
1259                try{
1260                    doChange (event);
1261                } catch (InvalidObjectException e) {
1262                    doRemove();
1263                    impl.setValid(false);
1264                }
1265                impl = null;
1266                source = null;
1267            } catch (RuntimeException JavaDoc e) {
1268                System.out.println("Exception in Listener.change () thrown !!"); // NOI18N
1269
e.printStackTrace ();
1270                throw e;
1271            }
1272        }
1273        
1274        public void doChange (MDRChangeEvent event) {
1275            if ((source instanceof Feature) && (event instanceof AttributeEvent) &&
1276                "javadoc".equals(((AttributeEvent) event).getAttributeName ())) { // NOI18N
1277
impl.fireJavaDocChange ();
1278            }
1279        }
1280        
1281    }
1282    
1283    // ..........................................................................
1284

1285    static class LWeakReference extends WeakReference JavaDoc implements Runnable JavaDoc {
1286        
1287        private static RequestProcessor CLEANUP_RP;
1288        
1289        private ElementListener listener;
1290        
1291        public LWeakReference (Object JavaDoc ref, ElementListener listener) {
1292            super (ref, Utilities.activeReferenceQueue ());
1293            this.listener = listener;
1294        }
1295        
1296        public void run() {
1297            synchronized(LWeakReference.class) {
1298                if (CLEANUP_RP == null) {
1299                    CLEANUP_RP = new RequestProcessor("Source hierarchy bridge"); // NOI18N
1300
}
1301            }
1302            CLEANUP_RP.post(new Runnable JavaDoc () {
1303                public void run() {
1304                    try {
1305                        listener.doRemove();
1306                    } catch (InvalidObjectException e) {
1307                    }
1308                }
1309            });
1310        }
1311        
1312    }
1313    
1314}
1315
Popular Tags