~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to xml/schema/schemaui/src/org/netbeans/modules/xml/schema/ui/nodes/SchemaComponentNode.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.netbeans.modules.xml.schema.ui.nodes;
 
43
 
 
44
import java.awt.Dialog;
 
45
import java.awt.datatransfer.Transferable;
 
46
import java.beans.FeatureDescriptor;
 
47
import java.beans.PropertyChangeEvent;
 
48
import java.beans.PropertyChangeListener;
 
49
import java.beans.PropertyEditor;
 
50
import java.beans.PropertyEditorSupport;
 
51
import java.io.IOException;
 
52
import java.lang.ref.SoftReference;
 
53
import java.lang.reflect.InvocationTargetException;
 
54
import java.util.ArrayList;
 
55
import java.util.Collections;
 
56
import java.util.LinkedList;
 
57
import java.util.List;
 
58
import java.util.Set;
 
59
import javax.swing.Action;
 
60
import org.netbeans.modules.refactoring.api.ui.RefactoringActionsFactory;
 
61
import org.netbeans.modules.xml.refactoring.spi.SharedUtils;
 
62
 
 
63
//import org.netbeans.modules.xml.refactoring.actions.RefactorAction;
 
64
import org.netbeans.modules.xml.refactoring.ui.ReferenceableProvider;
 
65
import org.netbeans.modules.xml.schema.model.Annotation;
 
66
import org.netbeans.modules.xml.schema.model.Documentation;
 
67
import org.netbeans.modules.xml.schema.model.ReferenceableSchemaComponent;
 
68
import org.netbeans.modules.xml.schema.model.Schema;
 
69
import org.netbeans.modules.xml.schema.model.SchemaComponent;
 
70
import org.netbeans.modules.xml.schema.model.SchemaComponentReference;
 
71
import org.netbeans.modules.xml.schema.model.SchemaModel;
 
72
import org.netbeans.modules.xml.schema.ui.basic.DesignGotoType;
 
73
import org.netbeans.modules.xml.schema.ui.basic.SchemaGotoType;
 
74
import org.netbeans.modules.xml.schema.ui.basic.SchemaSettings;
 
75
import org.netbeans.modules.xml.schema.ui.basic.UIUtilities;
 
76
import org.netbeans.modules.xml.xam.dom.Utils;
 
77
import org.netbeans.modules.xml.xam.ui.ComponentPasteType;
 
78
import org.netbeans.modules.xml.xam.ui.actions.GotoType;
 
79
import org.netbeans.modules.xml.xam.ui.actions.SourceGotoType;
 
80
import org.netbeans.modules.xml.xam.ui.actions.SuperGotoType;
 
81
import org.netbeans.modules.xml.xam.ui.cookies.GetComponentCookie;
 
82
import org.netbeans.modules.xml.xam.ui.cookies.GetSuperCookie;
 
83
import org.netbeans.modules.xml.xam.Component;
 
84
import org.netbeans.modules.xml.xam.ComponentEvent;
 
85
import org.netbeans.modules.xml.xam.ComponentListener;
 
86
import org.netbeans.modules.xml.xam.Nameable;
 
87
import org.netbeans.modules.xml.xam.Named;
 
88
import org.netbeans.modules.xml.xam.NamedReferenceable;
 
89
import org.netbeans.modules.xml.schema.ui.basic.ShowSchemaAction;
 
90
import org.netbeans.modules.xml.xam.ui.cookies.GotoCookie;
 
91
import org.netbeans.modules.xml.xam.ui.customizer.Customizer;
 
92
import org.netbeans.modules.xml.xam.ui.customizer.CustomizerProvider;
 
93
import org.netbeans.modules.xml.xam.ui.highlight.Highlight;
 
94
import org.netbeans.modules.xml.xam.ui.highlight.HighlightManager;
 
95
import org.netbeans.modules.xml.xam.ui.highlight.Highlighted;
 
96
import org.netbeans.modules.xml.schema.ui.basic.editors.StringEditor;
 
97
import org.netbeans.modules.xml.schema.ui.nodes.schema.properties.BaseSchemaProperty;
 
98
import org.netbeans.modules.xml.xam.ui.XAMUtils;
 
99
import org.netbeans.modules.xml.xam.ui.actions.GoToAction;
 
100
import org.netbeans.modules.xml.xam.ui.cookies.CountChildrenCookie;
 
101
import org.openide.DialogDescriptor;
 
102
import org.openide.DialogDisplayer;
 
103
import org.openide.ErrorManager;
 
104
import org.openide.actions.CopyAction;
 
105
import org.openide.actions.CutAction;
 
106
import org.openide.actions.DeleteAction;
 
107
import org.openide.actions.NewAction;
 
108
import org.openide.actions.PasteAction;
 
109
import org.openide.actions.PropertiesAction;
 
110
import org.openide.actions.ReorderAction;
 
111
import org.openide.explorer.propertysheet.ExPropertyEditor;
 
112
import org.openide.explorer.propertysheet.PropertyEnv;
 
113
import org.openide.filesystems.FileObject;
 
114
import org.openide.loaders.DataObject;
 
115
import org.openide.loaders.DataObjectNotFoundException;
 
116
import org.openide.nodes.AbstractNode;
 
117
import org.openide.nodes.Children;
 
118
import org.openide.nodes.Index;
 
119
import org.openide.nodes.Node;
 
120
import org.openide.nodes.Node.Property;
 
121
import org.openide.nodes.PropertySupport;
 
122
import org.openide.nodes.Sheet;
 
123
import org.openide.util.HelpCtx;
 
124
import org.openide.util.Lookup;
 
125
import org.openide.util.NbBundle;
 
126
import org.openide.util.WeakListeners;
 
127
import org.openide.util.actions.SystemAction;
 
128
import org.openide.util.datatransfer.NewType;
 
129
import org.openide.util.datatransfer.PasteType;
 
130
import org.openide.util.lookup.AbstractLookup;
 
131
import org.openide.util.lookup.InstanceContent;
 
132
import org.openide.util.lookup.Lookups;
 
133
import org.openide.util.lookup.ProxyLookup;
 
134
 
 
135
/**
 
136
 *
 
137
 * @author  Todd Fast, todd.fast@sun.com
 
138
 * @author  Nathan Fiedler
 
139
 */
 
140
public abstract class SchemaComponentNode<T extends SchemaComponent>
 
141
        extends AbstractNode
 
142
        implements Node.Cookie, ComponentListener, PropertyChangeListener,
 
143
        Highlighted, ReferenceableProvider, CountChildrenCookie,
 
144
        GetComponentCookie, GetSuperCookie, GotoCookie {
 
145
 
 
146
    /**
 
147
     *
 
148
     *
 
149
     */
 
150
    public SchemaComponentNode(SchemaUIContext context,
 
151
            SchemaComponentReference<T> reference, Children children) {
 
152
        this(context,reference,children,new InstanceContent());
 
153
    }
 
154
    
 
155
    
 
156
    /**
 
157
     * Constructor HACK to allow creating of our own lookup
 
158
     *
 
159
     */
 
160
    private SchemaComponentNode(SchemaUIContext context,
 
161
            SchemaComponentReference<T> reference, Children children,
 
162
            InstanceContent contents) {
 
163
        super(children, createLookup(context, contents));
 
164
        
 
165
        this.context=context;
 
166
        this.reference=reference;
 
167
        this.lookupContents=contents;
 
168
        
 
169
        // Add various objects to the lookup.
 
170
        contents.add(this);
 
171
        // Include the data object in order for the Navigator to
 
172
        // show the structure of the current document.
 
173
        DataObject dobj = getDataObject();
 
174
        if (dobj != null) {
 
175
            contents.add(dobj);
 
176
        }
 
177
        contents.add(context);
 
178
        contents.add(reference);
 
179
        contents.add(new DefaultExpandedCookie(false));
 
180
//        // add customizer provider if provided
 
181
//        CustomizerProvider provider = getCustomizerProvider();
 
182
//        if (provider != null) {
 
183
//            contents.add(provider);
 
184
//        }
 
185
        
 
186
        // reorder must be enabled only if its editable node
 
187
        if (children instanceof Index && isEditable() &&
 
188
                // dont show for schema bug 80138
 
189
                !(reference.get() instanceof Schema)) {
 
190
            contents.add(children);
 
191
        }
 
192
        T comp = reference.get();
 
193
        contents.add(comp);
 
194
        
 
195
        // Listen to changes in the model using a WeakListener. I hold onto
 
196
        // the WeakListener instance so I can explicitly remove it in the
 
197
        // destroy method.
 
198
        SchemaModel model = reference.get().getModel();
 
199
        if (model != null) {
 
200
            weakModelListener=
 
201
                    WeakListeners.propertyChange(this,model);
 
202
            model.addPropertyChangeListener(weakModelListener);
 
203
            weakComponentListener = (ComponentListener) WeakListeners.create(
 
204
                    ComponentListener.class, this, model);
 
205
            model.addComponentListener(weakComponentListener);
 
206
        }
 
207
        // Determine default names for the node
 
208
        if (comp instanceof Named) {
 
209
            // Just set the name, and let the method call below handle
 
210
            // the display name
 
211
            _setName(((Named) comp).getName());
 
212
        } else {
 
213
            _setName(comp.getPeer().getLocalName());
 
214
        }
 
215
        // Need a model for the following to work properly.
 
216
        if (model != null) {
 
217
            // Let the node try to update its display name
 
218
            updateDisplayName();
 
219
            // Let the node try to update its short desc
 
220
            updateShortDescription();
 
221
        }
 
222
 
 
223
        setIconBaseWithExtension(
 
224
                "org/netbeans/modules/xml/schema/ui/nodes/resources/"+
 
225
                "generic.png");
 
226
 
 
227
        referenceSet = Collections.singleton(
 
228
                (Component) ((SchemaComponentReference) reference).get());
 
229
        highlights = new LinkedList<Highlight>();
 
230
        HighlightManager.getDefault().addHighlighted(this);
 
231
    }
 
232
    
 
233
    /**
 
234
     * Create a lookup for this node, based on the given contents.
 
235
     *
 
236
     * @param  context   from which a Lookup is retrieved.
 
237
     * @param  contents  the basis of our new lookup.
 
238
     */
 
239
    private static Lookup createLookup(SchemaUIContext context,
 
240
            InstanceContent contents) {
 
241
        // We want our lookup to be based on the lookup from the context,
 
242
        // which provides a few necessary objects, such as a SaveCookie.
 
243
        // However, we do not want the Nodes or DataObjects, since we
 
244
        // provide our own.
 
245
        return new ProxyLookup(new Lookup[] {
 
246
            // Keep our lookup contents first, so that whatever we add to
 
247
            // the lookup is at the top of the lookup, such as this node,
 
248
            // which provides certain cookies, rather than that of the
 
249
            // currently selected node.
 
250
            new AbstractLookup(contents),
 
251
            Lookups.exclude(context.getLookup(), new Class[] {
 
252
                Node.class,
 
253
                DataObject.class,
 
254
            }),
 
255
        });
 
256
    }
 
257
    
 
258
    /**
 
259
     * Attempt to retrieve the DataObject associated with the model that
 
260
     * contains the component this node represents.
 
261
     *
 
262
     * @return  schema data object, if available, or null if not.
 
263
     */
 
264
    private DataObject getDataObject() {
 
265
        try {
 
266
            // Include the data object in order for the Navigator to
 
267
            // show the structure of the current document.
 
268
            SchemaModel model = reference.get().getModel();
 
269
            if (model != null) {
 
270
                FileObject fobj = (FileObject) model.getModelSource().
 
271
                        getLookup().lookup(FileObject.class);
 
272
                if (fobj != null) {
 
273
                    return DataObject.find(fobj);
 
274
                }
 
275
            }
 
276
        } catch (DataObjectNotFoundException donfe) {
 
277
            // fall through to return null
 
278
        }
 
279
        return null;
 
280
    }
 
281
    
 
282
    /**
 
283
     * This api returns the customizer provider.
 
284
     * Subclasses must override this api to return appropriate
 
285
     * customizer provider
 
286
     */
 
287
    protected CustomizerProvider getCustomizerProvider() {
 
288
        return null;
 
289
    }
 
290
    
 
291
    /**
 
292
     * Overriden to provide custom customizer.
 
293
     * Gets the customizer component from customizer provider
 
294
     * and displays it as modal dialog.
 
295
     * Subclasses who provide customizer should override
 
296
     * hasCustomizer and return true.
 
297
     */
 
298
    @Override
 
299
            public java.awt.Component getCustomizer() {
 
300
        if(!hasCustomizer()|| !isEditable()) return null;
 
301
        // get it from soft ref
 
302
        if(custRef==null || custRef.get()==null) {
 
303
            CustomizerProvider cp = getCustomizerProvider();
 
304
            if (cp==null) return null;
 
305
            Customizer cust = cp.getCustomizer();
 
306
            if (cust==null || cust.getComponent()==null) return null;
 
307
            custRef = new SoftReference<Customizer>(cust);
 
308
        } else {
 
309
            // might not be in sync so sync
 
310
            custRef.get().reset();
 
311
        }
 
312
        Customizer customizer = custRef.get();
 
313
        DialogDescriptor descriptor = UIUtilities.
 
314
                getCustomizerDialog(customizer,getTypeDisplayName(),isEditable());
 
315
        Dialog dlg = DialogDisplayer.getDefault().createDialog(descriptor);
 
316
        dlg.getAccessibleContext().setAccessibleDescription(dlg.getTitle());
 
317
        return dlg;
 
318
    }
 
319
    
 
320
    /**
 
321
     *
 
322
     *
 
323
     */
 
324
    public boolean equals(Object o) {
 
325
        // Without this, the tree view collapses when nodes are changed.
 
326
        if (o instanceof SchemaComponentNode) {
 
327
            SchemaComponentNode scn = (SchemaComponentNode) o;
 
328
            SchemaComponentReference scr = scn.getReference();
 
329
            return scr.equals(reference);
 
330
        }
 
331
        return false;
 
332
    }
 
333
    
 
334
    
 
335
    /**
 
336
     *
 
337
     *
 
338
     */
 
339
    public int hashCode() {
 
340
        // Without this, the tree view collapses when nodes are changed.
 
341
        return reference.hashCode();
 
342
    }
 
343
    
 
344
    
 
345
    /**
 
346
     *
 
347
     *
 
348
     */
 
349
    public SchemaUIContext getContext() {
 
350
        return context;
 
351
    }
 
352
    
 
353
    
 
354
    /**
 
355
     *
 
356
     *
 
357
     */
 
358
    public SchemaComponentReference<T> getReference() {
 
359
        return reference;
 
360
    }
 
361
    
 
362
    
 
363
    /**
 
364
     * Returns the contents of the lookup.  All cookies and other objects that
 
365
     * should be findable via the lookup should be added to this.
 
366
     *
 
367
     */
 
368
    protected InstanceContent getLookupContents() {
 
369
        return lookupContents;
 
370
    }
 
371
    
 
372
    /**
 
373
     * Determines if this node represents a component that is contained
 
374
     * in a valid (non-null) model.
 
375
     *
 
376
     * @return  true if model is valid, false otherwise.
 
377
     */
 
378
    protected boolean isValid() {
 
379
        return getReference().get().getModel() != null;
 
380
    }
 
381
    
 
382
    /**
 
383
     * Determines if this node represents a component that is contained
 
384
     * is editable
 
385
     *
 
386
     * @return  true if component is editable, false otherwise.
 
387
     */
 
388
    protected boolean isEditable() {
 
389
        SchemaModel model = getReference().get().getModel();
 
390
        return model != null && model == getContext().getModel() && 
 
391
                                XAMUtils.isWritable(model);
 
392
    }
 
393
    
 
394
    /**
 
395
     * Used by subclasses to update the display name as needed.  The default
 
396
     * implementation updates the display name for named schema components.
 
397
     * Note, this method may be called from the constructor, so be sure to
 
398
     * avoid using member variables!
 
399
     *
 
400
     */
 
401
    protected void updateDisplayName() {
 
402
        if (!isValid()) {
 
403
            // If there is no model, exceptions will occur.
 
404
            return;
 
405
        }
 
406
        T component = getReference().get();
 
407
        if (component instanceof Named) {
 
408
            String name=((Named)component).getName();
 
409
            // Automatically keep the name in sync for named schema components.
 
410
                        _setName(name);
 
411
                        if(name==null||name.equals("")) name = component.getPeer().getLocalName();
 
412
            setDisplayName(name);
 
413
        }
 
414
    }
 
415
    
 
416
    
 
417
    /**
 
418
     * updates the short descrption associated with node.
 
419
     * checks for if there is any annotation with documentation element.
 
420
     */
 
421
    private void updateShortDescription() {
 
422
        if (!isValid()) {
 
423
            // If there is no model, exceptions will occur.
 
424
            return;
 
425
        }
 
426
        T component = getReference().get();
 
427
        Documentation d = null;
 
428
        Annotation a = null;
 
429
        String language = SchemaSettings.getDefault().getLanguage();
 
430
        if (component instanceof Documentation) {
 
431
            d = (Documentation) component;
 
432
        } else if (component instanceof Annotation) {
 
433
            a = (Annotation) component;
 
434
        } else {
 
435
            a = component.getAnnotation();
 
436
        }
 
437
        if (a != null && !a.getDocumentationElements().isEmpty()) {
 
438
            if(language==null) {
 
439
                d = a.getDocumentationElements().iterator().next();
 
440
            } else {
 
441
                for (Documentation doc:a.getDocumentationElements()) {
 
442
                    if(language.equals(doc.getLanguage())) {
 
443
                        d = doc;
 
444
                        break;
 
445
                    }
 
446
                }
 
447
                if(d==null) {
 
448
                    d = a.getDocumentationElements().iterator().next();
 
449
                }
 
450
            }
 
451
        }
 
452
        if (d != null) {
 
453
            setShortDescription(d.getContentFragment());
 
454
        } else {
 
455
            setShortDescription(null);
 
456
        }
 
457
    }
 
458
    
 
459
    
 
460
    /**
 
461
     *
 
462
     *
 
463
     */
 
464
    public abstract String getTypeDisplayName();
 
465
    
 
466
    
 
467
    /**
 
468
     *
 
469
     *
 
470
     */
 
471
    protected String getHtmlTypeDisplayName() {
 
472
        return "<font color='#aaaaaa'>("+getTypeDisplayName()+")</font>";
 
473
    }
 
474
    
 
475
    
 
476
    /**
 
477
     *
 
478
     *
 
479
     */
 
480
    public boolean isDefaultExpanded() {
 
481
        DefaultExpandedCookie cookie=(DefaultExpandedCookie)
 
482
        getCookie(DefaultExpandedCookie.class);
 
483
        if (cookie!=null)
 
484
            return cookie.isDefaultExpanded();
 
485
        else
 
486
            return false;
 
487
    }
 
488
    
 
489
    
 
490
    /**
 
491
     *
 
492
     *
 
493
     */
 
494
    public void setDefaultExpanded(boolean value) {
 
495
        DefaultExpandedCookie cookie=(DefaultExpandedCookie)
 
496
        getCookie(DefaultExpandedCookie.class);
 
497
        if (cookie!=null)
 
498
            cookie.setDefaultExpanded(value);
 
499
    }
 
500
    
 
501
    
 
502
    /**
 
503
     * Finds the super definition of the schema component.
 
504
     * Returns null as default implementation
 
505
     * Subclasses which fave global type or reference definitions,
 
506
     * must override to return the global reference.
 
507
     */
 
508
    protected ReferenceableSchemaComponent getSuperDefinition() {
 
509
        return null;
 
510
    }
 
511
 
 
512
    public int getChildCount() {
 
513
        return getReference().get().getChildren().size();
 
514
    }
 
515
 
 
516
    public Component getComponent() {
 
517
        return getReference().get();
 
518
    }
 
519
 
 
520
    public Class<? extends Component> getComponentType() {
 
521
        return getReference().get().getComponentType();
 
522
    }
 
523
 
 
524
        // implementation of get super cookie
 
525
        public SchemaComponent getSuper()
 
526
        {
 
527
                return getSuperDefinition();
 
528
        }
 
529
  
 
530
    
 
531
    ////////////////////////////////////////////////////////////////////////////
 
532
    // Node methods
 
533
    ////////////////////////////////////////////////////////////////////////////
 
534
    
 
535
    /**
 
536
     *
 
537
     *
 
538
     */
 
539
    @Override
 
540
    public HelpCtx getHelpCtx() {
 
541
        if(this instanceof SchemaComponentNode)
 
542
            return new HelpCtx(SchemaComponentNode.class);
 
543
        return new HelpCtx(getClass());
 
544
    }
 
545
    
 
546
    
 
547
    /**
 
548
     *
 
549
     *
 
550
     */
 
551
    @Override
 
552
            public boolean canCut() {
 
553
        return isEditable();
 
554
    }
 
555
    
 
556
    
 
557
    /**
 
558
     *
 
559
     *
 
560
     */
 
561
    @Override
 
562
            public boolean canCopy() {
 
563
        return true;
 
564
    }
 
565
 
 
566
    @Override
 
567
    @SuppressWarnings("unchecked")
 
568
    protected void createPasteTypes(Transferable transferable, List list) {
 
569
        if (isValid() && isEditable()) {
 
570
            PasteType type = ComponentPasteType.getPasteType(
 
571
                    reference.get(), transferable, null);
 
572
            if (type != null) {
 
573
                list.add(type);
 
574
            }
 
575
        }
 
576
    }
 
577
 
 
578
    @Override
 
579
    public PasteType getDropType(Transferable transferable, int action, int index) {
 
580
        if (isValid() && isEditable()) {
 
581
            PasteType type = ComponentPasteType.getDropType(
 
582
                    reference.get(), transferable, null, action, index);
 
583
            if (type != null) {
 
584
                return type;
 
585
            }
 
586
        }
 
587
        return null;
 
588
    }
 
589
 
 
590
    @Override
 
591
            public boolean canDestroy() {
 
592
        SchemaComponent component = getReference().get();
 
593
        if (component instanceof Schema || !isEditable()) {
 
594
            return false;
 
595
        }
 
596
        return true;
 
597
    }
 
598
    
 
599
    
 
600
    /**
 
601
     *
 
602
     *
 
603
     */
 
604
    @Override
 
605
            public boolean canRename() {
 
606
        return supportsRename();
 
607
    }
 
608
    
 
609
    
 
610
    /**
 
611
     * Indicates if the component is nameable.
 
612
     *
 
613
     * @return  true if nameable, false otherwise.
 
614
     */
 
615
    private boolean isNameable() {
 
616
        // Need to check the component type instead of the component, to
 
617
        // avoid allowing rename of an element reference, in which the
 
618
        // implementation extends Nameable.
 
619
        return Nameable.class.isAssignableFrom(
 
620
                getReference().get().getComponentType());
 
621
    }
 
622
    
 
623
    
 
624
    /**
 
625
     * Indicates if the component can be renamed.
 
626
     *
 
627
     * @return  true if nameable, false otherwise.
 
628
     */
 
629
    private boolean supportsRename() {
 
630
        // check if its nameable and editable
 
631
        return isNameable() && isEditable();
 
632
    }
 
633
    
 
634
    
 
635
    /**
 
636
     * Set the name property directly without adjusting the associated model
 
637
     *
 
638
     */
 
639
    private void _setName(String value) {
 
640
                // prevent NPE from explorermanager
 
641
                if(value==null) value="";
 
642
        super.setName(value);
 
643
    }
 
644
    
 
645
    
 
646
    /**
 
647
     *
 
648
     *
 
649
     */
 
650
    @Override
 
651
            public void setName(String value) {
 
652
                NamedReferenceable ref = getReferenceable();
 
653
                if(ref==null)
 
654
                {
 
655
                        _setName(value);
 
656
                        if (supportsRename())
 
657
                        {
 
658
                                try
 
659
                                {
 
660
                                        getReference().get().getModel().startTransaction();
 
661
                                        Nameable n = (Nameable)getReference().get();
 
662
                                        n.setName(value);
 
663
                                }
 
664
                                finally
 
665
                                {
 
666
                                getReference().get().getModel().endTransaction();
 
667
                                }
 
668
                        }
 
669
                }
 
670
                else
 
671
                {
 
672
            SharedUtils.locallyRenameRefactor((Nameable)ref, value);
 
673
                }
 
674
    }
 
675
    
 
676
    /**
 
677
     * Checks for references to this component, and if none are found,
 
678
     * remove it from the model.
 
679
     */
 
680
    public void destroy() throws IOException {
 
681
        SchemaModel model = getReference().get().getModel();
 
682
        if(model == null) {
 
683
            // fix bug 6421899
 
684
            // this node might have been deleted from model as a result of
 
685
            // deletion of its parent. get model from context and remove
 
686
            // listeners. no need to remove it again from model.
 
687
            model = getContext().getModel();
 
688
            model.removeComponentListener(weakComponentListener);
 
689
            model.removePropertyChangeListener(weakModelListener);
 
690
        } else {
 
691
            model.removeComponentListener(weakComponentListener);
 
692
            model.removePropertyChangeListener(weakModelListener);
 
693
            
 
694
            // Remove the component from the model.
 
695
            SchemaComponent component = getReference().get();
 
696
            try {
 
697
                model.startTransaction();
 
698
                model.removeChildComponent(component);
 
699
                //need to provide a hook
 
700
                cleanup();
 
701
            } finally {
 
702
                model.endTransaction();
 
703
            }
 
704
        }
 
705
        super.destroy();
 
706
    }
 
707
 
 
708
    /**
 
709
     * This is a hook for the subclasses if they want to do something special in the same transaction.
 
710
     * For example, when an import gets deleted, we should also remove the namespace declaration.
 
711
     */
 
712
    protected void cleanup() {
 
713
        //default implementation needs to be empty
 
714
        //subclasses should override, if they want do extra stuff inside the same transaction.
 
715
        //See AdvancedImportNode for details.
 
716
    }
 
717
    
 
718
    
 
719
    /**
 
720
     *
 
721
     *
 
722
     */
 
723
    @Override
 
724
            protected Sheet createSheet() {
 
725
        super.createSheet();
 
726
        Sheet sheet=Sheet.createDefault();
 
727
        Sheet.Set set=sheet.get(Sheet.PROPERTIES);
 
728
        set.put(
 
729
                new PropertySupport("kind",String.class,
 
730
                NbBundle.getMessage(SchemaComponentNode.class,
 
731
                "PROP_SchemaComponentNode_Kind"),
 
732
                "",true,false) {
 
733
            public Object getValue() {
 
734
                return getTypeDisplayName();
 
735
            }
 
736
            
 
737
            public void setValue(Object value) {
 
738
                // Not modifiable
 
739
            }
 
740
        });
 
741
        
 
742
        try {
 
743
            // id property
 
744
            Property idProperty = new BaseSchemaProperty((SchemaComponent)getReference().get(),
 
745
                    String.class,
 
746
                    SchemaComponent.ID_PROPERTY,
 
747
                    NbBundle.getMessage(SchemaComponentNode.class,
 
748
                    "PROP_SchemaComponentNode_ID"),
 
749
                    NbBundle.getMessage(SchemaComponentNode.class,
 
750
                    "PROP_SchemaComponentNode_IDDesc"),
 
751
                    StringEditor.class
 
752
                    ){
 
753
                public void setValue(Object o) throws
 
754
                        IllegalAccessException, InvocationTargetException {
 
755
                    if (o instanceof String) {
 
756
                        if("".equals(o)) {
 
757
                            super.setValue(null);
 
758
                        } else if (Utils.isValidNCName(o.toString())){
 
759
                            super.setValue(o);                        
 
760
                        } else {
 
761
                            String msg = NbBundle.getMessage(BaseSchemaProperty.class, 
 
762
                                    "MSG_Neg_Int_Value", o); //NOI18N
 
763
                            IllegalArgumentException iae = new IllegalArgumentException(msg);
 
764
                            ErrorManager.getDefault().annotate(iae, ErrorManager.USER,
 
765
                                    msg, msg, null, new java.util.Date());
 
766
                            throw iae;
 
767
                        }
 
768
                    } else {
 
769
                        super.setValue(o);
 
770
                    }
 
771
                }
 
772
            };
 
773
            set.put(new SchemaModelFlushWrapper(getReference().get(), idProperty));
 
774
        } catch (NoSuchMethodException nsme) {
 
775
            assert false: "properties must be defined";
 
776
        }
 
777
        
 
778
        // If we are a named node, display that in the property sheet
 
779
        if (isNameable())
 
780
            set.put(new PropertySupport.Name(this));
 
781
        
 
782
        if(hasCustomizer()&&isEditable()) {
 
783
            Property structureProp = new PropertySupport.ReadWrite("structure", //NOI18N
 
784
                    String.class,
 
785
                    NbBundle.getMessage(SchemaComponentNode.class,
 
786
                    "PROP_SchemaComponentNode_Customize"),
 
787
                    NbBundle.getMessage(SchemaComponentNode.class,
 
788
                    "PROP_SchemaComponentNode_Customize_ShortDesc")) {
 
789
                public Object getValue() throws IllegalAccessException,InvocationTargetException {
 
790
                    return NbBundle.getMessage(SchemaComponentNode.class,
 
791
                            "PROP_SchemaComponentNode_Customize_Label");
 
792
                }
 
793
                public void setValue(Object val) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException {
 
794
                }
 
795
                public PropertyEditor getPropertyEditor() {
 
796
                    return new StructurePropertyEditor();
 
797
                }
 
798
            };
 
799
            set.put(structureProp);
 
800
        }
 
801
 
 
802
        return sheet;
 
803
    }
 
804
 
 
805
    /**
 
806
     * Indicates if this node should allow reordering of its children.
 
807
     * The default implementation allows reordering only if there is
 
808
     * more than one child component.
 
809
     *
 
810
     * @return  true if reordering of this node's children is permitted.
 
811
     */
 
812
    protected boolean allowReordering() {
 
813
        // Check if we have more than one physical child in the model.
 
814
        // Using the node children count results in too many index out
 
815
        // of bounds exceptions.
 
816
        return getReference().get().getChildren().size() > 1;
 
817
    }
 
818
 
 
819
    @Override
 
820
    public Action[] getActions(boolean context) {
 
821
        ReadOnlyCookie roc = (ReadOnlyCookie) getContext().getLookup().lookup(
 
822
                ReadOnlyCookie.class);
 
823
        List<Action> actions = new ArrayList<Action>();
 
824
        if (roc != null && roc.isReadOnly()) {
 
825
            // Set of actions for read-only components.
 
826
            actions.add(SystemAction.get(GoToAction.class));
 
827
        } else {
 
828
            // Set of actions for modifiable components.
 
829
            actions.add(SystemAction.get(CutAction.class));
 
830
            actions.add(SystemAction.get(CopyAction.class));
 
831
            actions.add(SystemAction.get(PasteAction.class));
 
832
            actions.add(null);
 
833
            actions.add(SystemAction.get(NewAction.class));
 
834
            actions.add(SystemAction.get(DeleteAction.class));
 
835
            if (allowReordering()) {
 
836
                actions.add(SystemAction.get(ReorderAction.class));
 
837
            }
 
838
            actions.add(null);
 
839
            actions.add(SystemAction.get(GoToAction.class));
 
840
            //actions.add(SystemAction.get(FindUsagesAction.class));
 
841
            //new action based on new refactoring API
 
842
            actions.add(RefactoringActionsFactory.whereUsedAction());
 
843
            actions.add(null);
 
844
            //actions.add(SystemAction.get(RefactorAction.class));
 
845
            //new action based on new refactoring API
 
846
            actions.add(RefactoringActionsFactory.editorSubmenuAction());
 
847
            actions.add(null);
 
848
            actions.add(SystemAction.get(PropertiesAction.class));
 
849
        }
 
850
        return actions.toArray(new Action[actions.size()]);
 
851
    }
 
852
    
 
853
    @Override
 
854
    public Action getPreferredAction() {
 
855
        // This exists for use in the Navigator.
 
856
        ReadOnlyCookie roc = (ReadOnlyCookie) getCookie(ReadOnlyCookie.class);
 
857
        if (roc != null && roc.isReadOnly()) {
 
858
            return SystemAction.get(ShowSchemaAction.class);
 
859
        }
 
860
        return super.getPreferredAction();
 
861
    }
 
862
    
 
863
    
 
864
   /**
 
865
     * This api returns the factory which gives back new types for this node.
 
866
     * Default FactoryImpl provides addition of annotation.
 
867
     * Subclasses can override this api to allow addition of their allowed
 
868
     * child types.
 
869
     */
 
870
    protected NewTypesFactory getNewTypesFactory() {
 
871
        return new NewTypesFactory();
 
872
    }
 
873
    
 
874
    public final NewType[] getNewTypes() {
 
875
        if(isEditable()) {
 
876
            return getNewTypesFactory().getNewTypes(getReference(), null);
 
877
        }
 
878
        return new NewType[] {};
 
879
    }
 
880
 
 
881
    public GotoType[] getGotoTypes() {
 
882
        return GOTO_TYPES;
 
883
    }
 
884
 
 
885
    ////////////////////////////////////////////////////////////////////////////
 
886
    // Listener methods
 
887
    ////////////////////////////////////////////////////////////////////////////
 
888
    
 
889
    public void childrenAdded(ComponentEvent evt) {
 
890
        if (isValid()) {
 
891
                        if(evt.getSource() == getReference().get())
 
892
                        {
 
893
                                ((RefreshableChildren) getChildren()).refreshChildren();
 
894
                        }
 
895
                        if(evt.getSource() == getReference().get() ||
 
896
                                        evt.getSource() == getReference().get().getAnnotation())
 
897
                        {
 
898
                                updateShortDescription();
 
899
                        }
 
900
        }
 
901
    }
 
902
    
 
903
    public void childrenDeleted(ComponentEvent evt) {
 
904
        if (isValid()) {
 
905
                        if(evt.getSource() == getReference().get())
 
906
                        {
 
907
                                ((RefreshableChildren) getChildren()).refreshChildren();
 
908
                        }
 
909
                        if(evt.getSource() == getReference().get() ||
 
910
                                        evt.getSource() == getReference().get().getAnnotation())
 
911
                        {
 
912
                                updateShortDescription();
 
913
                        }
 
914
        }
 
915
    }
 
916
    
 
917
    public void valueChanged(ComponentEvent evt) {
 
918
                if (isValid())
 
919
                {
 
920
                        T component = getReference().get();
 
921
                        if(evt.getSource() == component)
 
922
                        {
 
923
                                updateDisplayName();
 
924
                        }
 
925
                        Documentation d = null;
 
926
                        if(component instanceof Documentation)
 
927
                                d = (Documentation)component;
 
928
                        else if(component instanceof Annotation)
 
929
                        {
 
930
                                Annotation a = (Annotation)component;
 
931
                                if(!a.getDocumentationElements().isEmpty())
 
932
                                        d = a.getDocumentationElements().iterator().next();
 
933
                        } 
 
934
                        else
 
935
                        {
 
936
                                Annotation a = component.getAnnotation();
 
937
                                if(a!=null && !a.getDocumentationElements().isEmpty())
 
938
                                        d = a.getDocumentationElements().iterator().next();
 
939
                        }
 
940
                        if(evt.getSource()==d)
 
941
                        {
 
942
                                updateShortDescription();
 
943
                        }
 
944
                }
 
945
    }
 
946
    
 
947
    /**
 
948
     * Reacts to granular property change events from model.
 
949
     * Updates displayname if needed.
 
950
     * Fires properties changed events if needed.
 
951
     * Subclasses override if needed.
 
952
     */
 
953
    public void propertyChange(PropertyChangeEvent event) {
 
954
        if (isValid() && event.getSource() == getReference().get()) {
 
955
            try {
 
956
                updateDisplayName();
 
957
                String propName = event.getPropertyName();
 
958
                Sheet.Set propertySet = getSheet().get(Sheet.PROPERTIES);
 
959
                if(propertySet!=null){
 
960
                    if (propertySet.get(propName)!=null) {
 
961
                        firePropertyChange(propName,event.getOldValue(),
 
962
                                event.getNewValue());
 
963
                    }
 
964
                    else {
 
965
                        ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL,
 
966
                                propName + " property is not defined in " +
 
967
                                getTypeDisplayName());
 
968
                    }
 
969
                }
 
970
            } catch (IllegalStateException ise) {
 
971
                // Component is not in the model.
 
972
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ise);
 
973
            } catch (NullPointerException npe) {
 
974
                // Does not reproduce reliably, but catch and log regardless.
 
975
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, npe);
 
976
            }
 
977
        }
 
978
    }
 
979
 
 
980
    public Set<Component> getComponents() {
 
981
        return referenceSet;
 
982
    }
 
983
    
 
984
    public void highlightAdded(Highlight hl) {
 
985
        highlights.add(hl);
 
986
        fireDisplayNameChange("TempName", getDisplayName());
 
987
    }
 
988
    
 
989
    public void highlightRemoved(Highlight hl) {
 
990
        highlights.remove(hl);
 
991
        fireDisplayNameChange("TempName", getDisplayName());
 
992
    }
 
993
    
 
994
    /**
 
995
     * Given a display name, add the appropriate HTML tags to highlight
 
996
     * the display name as dictated by any Highlights associated with
 
997
     * this node.
 
998
     *
 
999
     * @param  name  current display name.
 
1000
     * @return  marked up display name.
 
1001
     */
 
1002
    protected String applyHighlights(String name) {
 
1003
        int count = highlights.size();
 
1004
        if (count > 0) {
 
1005
            // Apply the last highlight that was added to our list.
 
1006
            Highlight hl = highlights.get(count - 1);
 
1007
            String type = hl.getType();
 
1008
            String code = null;
 
1009
 
 
1010
 
 
1011
            if (type.equals(Highlight.SEARCH_RESULT)) {
 
1012
                code = "e68b2c";
 
1013
            } else if (type.equals(Highlight.SEARCH_RESULT_PARENT)) {
 
1014
                code = "ffc73c";
 
1015
            }  else if (type.equals(Highlight.FIND_USAGES_RESULT_PARENT)) {
 
1016
                code = "B5E682";    // was c7ff3c chartreuse
 
1017
            }else if (type.equals(Highlight.FIND_USAGES_RESULT)) {
 
1018
                code = "8be62c";    // darker green
 
1019
            }
 
1020
            name = "<strong><font color=\"#" + code + "\">" + name +
 
1021
                    "</font></strong>";
 
1022
        }
 
1023
        return name;
 
1024
    }
 
1025
    
 
1026
    public String getDisplayName() {
 
1027
        String instanceName = getDefaultDisplayName();
 
1028
        return instanceName.length()==0 ? instanceName : 
 
1029
               instanceName + " " + "[" + getTypeDisplayName() + "]"; // NOI18N
 
1030
    }
 
1031
    
 
1032
    public String getDefaultDisplayName() {
 
1033
        String instanceName = super.getDisplayName();
 
1034
        return instanceName == null || instanceName.length() == 0
 
1035
            ? "" : instanceName; 
 
1036
    }
 
1037
    
 
1038
    public String getHtmlDisplayName() {
 
1039
        String name = getDefaultDisplayName();
 
1040
        // Need to escape any HTML meta-characters in the name.
 
1041
        if(name!=null)
 
1042
            name = name.replace("<", "&lt;").replace(">", "&gt;");
 
1043
        return applyHighlights(name);
 
1044
    }
 
1045
    
 
1046
    ////////////////////////////////////////////////////////////////////////////
 
1047
    // Inner class
 
1048
    ////////////////////////////////////////////////////////////////////////////
 
1049
    
 
1050
    private class StructurePropertyEditor extends PropertyEditorSupport
 
1051
            implements ExPropertyEditor {
 
1052
        public boolean supportsCustomEditor() {
 
1053
            return true;
 
1054
        }
 
1055
        
 
1056
        public java.awt.Component getCustomEditor() {
 
1057
            return getCustomizer();
 
1058
        }
 
1059
        
 
1060
        public void attachEnv(PropertyEnv env ) {
 
1061
            FeatureDescriptor desc = env.getFeatureDescriptor();
 
1062
            desc.setValue("canEditAsText", Boolean.FALSE); // NOI18N
 
1063
        }
 
1064
    }
 
1065
 
 
1066
    private SchemaUIContext context;
 
1067
    private SchemaComponentReference<T> reference;
 
1068
    private Set<Component> referenceSet;
 
1069
    /** Ordered list of highlights applied to this node. */
 
1070
    private List<Highlight> highlights;
 
1071
    private InstanceContent lookupContents;
 
1072
    private PropertyChangeListener modelListener;
 
1073
    private PropertyChangeListener weakModelListener;
 
1074
    private ComponentListener weakComponentListener;
 
1075
    private SoftReference<Customizer> custRef;
 
1076
    private static final GotoType[] GOTO_TYPES = new GotoType[] {
 
1077
        new SourceGotoType(),
 
1078
        new SchemaGotoType(),
 
1079
        new DesignGotoType(),
 
1080
        new SuperGotoType(),
 
1081
    };
 
1082
 
 
1083
    /**
 
1084
     * Implement ReferenceableProvider
 
1085
     * 
 
1086
     * 
 
1087
     * @returns NamedReferenceable used by Refactoring Find Usage, Safe Delete, 
 
1088
     *          and Rename
 
1089
     */
 
1090
    public NamedReferenceable getReferenceable() {
 
1091
        SchemaComponent comp = reference.get();
 
1092
        if (comp instanceof NamedReferenceable && isValid() && comp.getModel().
 
1093
                getModelSource().getLookup().lookup(FileObject.class) != null){
 
1094
            return NamedReferenceable.class.cast(comp);
 
1095
        }
 
1096
        return null;
 
1097
    }
 
1098
    
 
1099
    /**
 
1100
     * This api is used to set the back pointer to the ReadOnlySchemaComponentNode,
 
1101
     * which represents this node on UI in case of refrenced components.
 
1102
     */
 
1103
    public void setReferencingNode(final Node referencingNode) {
 
1104
        getLookupContents().add(
 
1105
                new ReferencingNodeProvider() {
 
1106
            public Node getNode() {
 
1107
                return referencingNode;
 
1108
            }
 
1109
        });
 
1110
    }
 
1111
}