001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.xml.schema.abe.nodes;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.IOException;
047: import javax.swing.Action;
048: import org.netbeans.modules.refactoring.api.ui.RefactoringActionsFactory;
049: import org.netbeans.modules.xml.axi.AXIComponent;
050: import org.netbeans.modules.xml.axi.AXIComponent.ComponentType;
051: import org.netbeans.modules.xml.axi.AXIContainer;
052: import org.netbeans.modules.xml.axi.AXIModel;
053: import org.netbeans.modules.xml.axi.Attribute;
054: import org.netbeans.modules.xml.axi.ContentModel;
055: import org.netbeans.modules.xml.axi.Element;
056: import org.netbeans.modules.xml.axi.datatype.Datatype;
057: import org.netbeans.modules.xml.refactoring.CannotRefactorException;
058: import org.netbeans.modules.xml.refactoring.spi.SharedUtils;
059: import org.netbeans.modules.xml.refactoring.ui.ReferenceableProvider;
060: import org.netbeans.modules.xml.schema.abe.InstanceDesignConstants;
061: import org.netbeans.modules.xml.schema.abe.InstanceUIContext;
062: import org.netbeans.modules.xml.schema.abe.StartTagPanel;
063: import org.netbeans.modules.xml.schema.abe.UIUtilities;
064: import org.netbeans.modules.xml.schema.abe.action.ShowDesignAction;
065: import org.netbeans.modules.xml.schema.model.SchemaComponent;
066: import org.netbeans.modules.xml.schema.model.SchemaModel;
067: import org.netbeans.modules.xml.schema.ui.basic.SchemaGotoType;
068: import org.netbeans.modules.xml.xam.Component;
069: import org.netbeans.modules.xml.xam.Nameable;
070: import org.netbeans.modules.xml.xam.NamedReferenceable;
071: import org.netbeans.modules.xml.xam.ui.XAMUtils;
072: import org.netbeans.modules.xml.xam.ui.actions.GoToAction;
073: import org.netbeans.modules.xml.xam.ui.actions.GotoType;
074: import org.netbeans.modules.xml.xam.ui.actions.SourceGotoType;
075: import org.netbeans.modules.xml.xam.ui.cookies.GetSuperCookie;
076: import org.netbeans.modules.xml.xam.ui.cookies.GotoCookie;
077: import org.openide.ErrorManager;
078: import org.openide.actions.DeleteAction;
079: import org.openide.actions.NewAction;
080: import org.openide.actions.PropertiesAction;
081: import org.openide.filesystems.FileObject;
082: import org.openide.loaders.DataObject;
083: import org.openide.nodes.AbstractNode;
084: import org.openide.nodes.Children;
085: import org.openide.nodes.Node;
086: import org.openide.nodes.Sheet;
087: import org.openide.util.HelpCtx;
088: import org.openide.util.Lookup;
089: import org.openide.util.NbBundle;
090: import org.openide.util.WeakListeners;
091: import org.openide.util.actions.SystemAction;
092: import org.openide.util.datatransfer.NewType;
093: import org.openide.util.lookup.AbstractLookup;
094: import org.openide.util.lookup.InstanceContent;
095: import org.openide.util.lookup.Lookups;
096: import org.openide.util.lookup.ProxyLookup;
097:
098: /**
099: * Base class of all ABE nodes. Each node is associated with some
100: * AXI component. However, there are two ways to obtain the axi component
101: * from these nodes.
102: *
103: * 1. Call getAXIComponent()
104: * 2. Find it from the node's lookup()
105: *
106: * getAXIComponent() will return the component this node is associated with.
107: * In contrast, the component that you get from lookup is always the original.
108: * IN AXIOM there can be proxy components that act on behalf of an original or
109: * shared component. For example, lets say PO.xsd declares an element 'shipTo' and
110: * the type of this eleemnt is from a differnet source file Address.xsd.
111: *
112: * @author Samaresh (Samaresh.Panda@Sun.Com)
113: */
114: public abstract class ABEAbstractNode extends AbstractNode implements
115: GotoCookie, PropertyChangeListener, ReferenceableProvider {
116:
117: private InstanceUIContext context;
118: private AXIComponent axiComponent;
119: private Datatype datatype;
120: boolean uiNode = false;
121: private InstanceContent icont = new InstanceContent();
122: private boolean readOnly = false;
123:
124: /**
125: * Creates a new instance of ABEAbstractNode
126: */
127: public ABEAbstractNode(AXIComponent axiComponent,
128: InstanceUIContext instanceUIContext) {
129: this (axiComponent, Children.LEAF, instanceUIContext,
130: new InstanceContent());
131: }
132:
133: public ABEAbstractNode(AXIComponent axiComponent, Children children) {
134: this (axiComponent, children, null, new InstanceContent());
135: }
136:
137: private ABEAbstractNode(AXIComponent axiComponent,
138: Children children,
139: final InstanceUIContext instanceUIContext,
140: InstanceContent icont) {
141: super (children, createLookup(axiComponent, icont,
142: instanceUIContext));
143: this .icont = icont;
144: this .setAXIComponent(axiComponent);
145: this .setContext(instanceUIContext);
146: if (instanceUIContext != null) {
147: uiNode = true;
148: this .icont.add(instanceUIContext);
149: instanceUIContext
150: .addPropertyChangeListener(new PropertyChangeListener() {
151: public void propertyChange(
152: PropertyChangeEvent evt) {
153: if (evt
154: .getPropertyName()
155: .equals(
156: InstanceDesignConstants.PROP_SHUTDOWN)) {
157: ABEAbstractNode.this .icont
158: .remove(instanceUIContext);
159: ABEAbstractNode.this .axiComponent = null;
160: }
161: }
162: });
163: }
164: this .icont.add(this );
165: }
166:
167: /**
168: * This was added for go to source.
169: * Keep the axiComponent in the node's lookup. If the component
170: * is a proxy, keep the original or shared component.
171: */
172: private static Lookup createLookup(final AXIComponent component,
173: InstanceContent icont, InstanceUIContext context) {
174: AXIComponent lookupComponent = component;
175: if (component.getComponentType() == ComponentType.PROXY)
176: lookupComponent = component.getOriginal();
177: final AXIComponent tmpLookupComponent = component;
178: Lookup doLookup = null;
179: if (context != null) {
180: doLookup = context.getSchemaDataObject().getNodeDelegate()
181: .getLookup();
182: doLookup = Lookups.exclude(doLookup,
183: new Class[] { Node.class });
184: }
185:
186: return new ProxyLookup(new Lookup[] {
187: // schemamodel lookup
188: // exclude the DataObject here because the DataObject for the
189: // model this node is displayed in will be coming from the
190: // NodeDelegate. If this is not done there end up being two
191: // DataObjects in the lookup and this may cause a problem with
192: // save cookies, etc.
193: Lookups.exclude(component.getModel().getSchemaModel()
194: .getModelSource().getLookup(),
195: new Class[] { DataObject.class }),
196: // axi component
197: Lookups.singleton(lookupComponent),
198: Lookups.singleton(new GetSuperCookie() {
199: // this is for go to super definition.
200: public Component getSuper() {
201: return UIUtilities
202: .getSuperDefn(tmpLookupComponent);
203: }
204: }),
205: //Schema DO's lookup
206: (doLookup == null ? Lookup.EMPTY : doLookup),
207: //and misc
208: new AbstractLookup(icont) });
209: }
210:
211: public void showSuperDefinition() {
212: InstanceUIContext context = (InstanceUIContext) getLookup()
213: .lookup(InstanceUIContext.class);
214: UIUtilities.showDefinition(context, getAXIComponent(), true);
215: }
216:
217: /**
218: *
219: *
220: */
221: @Override
222: public HelpCtx getHelpCtx() {
223: if (this instanceof ABEAbstractNode)
224: return new HelpCtx(ABEAbstractNode.class);
225: return new HelpCtx(getClass());
226: }
227:
228: /**
229: * Overwrites AbstractNode's createSheet to allow the creation of sheet.
230: */
231: protected Sheet createSheet() {
232: Sheet sheet = super .createSheet();
233: populateProperties(sheet);
234: return sheet;
235: }
236:
237: protected abstract void populateProperties(Sheet sheet);
238:
239: protected abstract String getTypeDisplayName();
240:
241: public String getDisplayName() {
242: String instanceName = super .getDisplayName();
243: return ((instanceName == null || instanceName.length() == 0) ? ""
244: : instanceName + " ")
245: + "[" + getTypeDisplayName() + "]"; // NOI18N
246: }
247:
248: public final String getHtmlDisplayName() {
249: return super .getDisplayName();
250: }
251:
252: public Action[] getActions(boolean b) {
253: if (uiNode) {
254: if (getAXIComponent().isReadOnly()) {
255: //filter out refactor action if this is a readonly file
256: SystemAction[] ret = new SystemAction[ALL_ACTIONS.length];
257: for (int i = 0; i < ALL_ACTIONS.length; i++) {
258: String name = null;
259: if (ALL_ACTIONS[i] != null)
260: name = (String) ALL_ACTIONS[i]
261: .getValue(Action.NAME);
262:
263: if (name != null && name.equals("Refactor")) {
264: ret[i] = null;
265: } else {
266: ret[i] = ALL_ACTIONS[i];
267: }
268: }
269: return ret;
270: }
271: return ALL_ACTIONS;
272: } else
273: return SUB_ACTIONS;
274: }
275:
276: private static final GotoType[] GOTO_TYPES = new GotoType[] {
277: new SourceGotoType(), new SchemaGotoType(), };
278:
279: private static final SystemAction[] ALL_ACTIONS = new SystemAction[] {
280: /*SystemAction.get(CutAction.class),
281: SystemAction.get(CopyAction.class),
282: SystemAction.get(PasteAction.class),
283: null,*/
284: SystemAction.get(NewAction.class),
285: SystemAction.get(DeleteAction.class),
286: null,
287: SystemAction.get(GoToAction.class),
288: null,
289: (SystemAction) RefactoringActionsFactory.whereUsedAction(),
290: (SystemAction) RefactoringActionsFactory
291: .editorSubmenuAction(), null,
292: SystemAction.get(PropertiesAction.class) };
293:
294: private static final SystemAction[] SUB_ACTIONS = new SystemAction[] { SystemAction
295: .get(GoToAction.class), };
296:
297: public InstanceUIContext getContext() {
298: return context;
299: }
300:
301: public void setContext(InstanceUIContext context) {
302: this .context = context;
303: }
304:
305: public AXIComponent getAXIComponent() {
306: return axiComponent;
307: }
308:
309: private void setAXIComponent(AXIComponent axiComponent) {
310: this .axiComponent = axiComponent;
311: axiComponent.getModel().addPropertyChangeListener(
312: WeakListeners.propertyChange(this , axiComponent
313: .getModel()));
314: }
315:
316: public Datatype getDatatype() {
317: return datatype;
318: }
319:
320: public void propertyChange(PropertyChangeEvent evt) {
321: if (evt.getSource() == axiComponent
322: && evt.getPropertyName().equals(AXIContainer.PROP_NAME)) {
323: Object oldValue = evt.getOldValue();
324: String oldDisplayName = oldValue == null ? null : oldValue
325: .toString();
326: fireDisplayNameChange(oldDisplayName, getDisplayName());
327: }
328: }
329:
330: public Action getPreferredAction() {
331: return SystemAction.get(ShowDesignAction.class);
332: }
333:
334: public NewType[] getNewTypes() {
335: return new NewType[0];
336: }
337:
338: public GotoType[] getGotoTypes() {
339: return GOTO_TYPES;
340: }
341:
342: public void remove() {
343: if (!canWrite())
344: return;
345: UIUtilities.setBusyCursor(context);
346: try {
347: if (getReferenceable() != null) {
348: safeDelete();
349: return;
350: }
351: //use normal delete
352: doDelete();
353: } finally {
354: UIUtilities.setDefaultCursor(context);
355: }
356: }
357:
358: private void doDelete() {
359: if (getAXIComponent() == null
360: || getAXIComponent().getModel() == null)
361: return;
362: AXIModel model = getAXIComponent().getModel();
363: model.startTransaction();
364: try {
365: getAXIComponent().getParent()
366: .removeChild(getAXIComponent());
367: } finally {
368: model.endTransaction();
369: }
370: }
371:
372: public boolean isReadOnly() {
373: return readOnly;
374: }
375:
376: public void setReadOnly(boolean readOnly) {
377: this .readOnly = readOnly;
378: }
379:
380: public boolean canDestroy() {
381: return canWrite();
382: }
383:
384: public void destroy() throws IOException {
385: super .destroy();
386: remove();
387: }
388:
389: public boolean canWrite() {
390: // Check for null model since component may have been removed.
391: AXIComponent c = getAXIComponent();
392: if (c == null || (c.getModel() == null))
393: return false;
394:
395: AXIComponent o = c.getOriginal();
396: if (c != o && c.isReadOnly()) {
397: return false;
398: }
399:
400: SchemaModel model = c.getModel().getSchemaModel();
401: return XAMUtils.isWritable(model);
402: }
403:
404: public void setName(String name) {
405: if (name == null || getName().equals(name))
406: return;
407: InstanceUIContext context = (InstanceUIContext) this
408: .getLookup().lookup(InstanceUIContext.class);
409: AXIComponent axiComponent = getAXIComponent();
410: if (canWrite()) {
411: if (org.netbeans.modules.xml.xam.dom.Utils
412: .isValidNCName(name)) {
413: //call refactoring.
414: UIUtilities.setBusyCursor(context);
415: try {
416: setNameByRefactoring(name);
417: } finally {
418: //reset the wait cursor
419: UIUtilities.setDefaultCursor(context);
420: }
421: } else {
422: if (context != null) {
423: String errorMessage = NbBundle.getMessage(
424: StartTagPanel.class, "MSG_NOT_A_NCNAME");
425: UIUtilities.showErrorMessage(errorMessage, context);
426: }
427: }
428: } else {
429: if (context != null) {
430: String errorMessage = NbBundle.getMessage(
431: ABEAbstractNode.class, "MSG_IS_READONLY");
432: UIUtilities.showErrorMessage(errorMessage, context);
433: }
434: }
435: }
436:
437: public void setNameByRefactoring(String value) {
438: NamedReferenceable ref = getReferenceable();
439: if (ref == null) {
440: //since this is a ref, it can not be renamed here.
441: //this often happens for references
442: /*setNameInModel(value);
443: return;*/
444: AXIComponent axiComponent = getAXIComponent();
445: if ((axiComponent instanceof Element)
446: || (axiComponent instanceof Attribute)) {
447: if (axiComponent instanceof Element) {
448: axiComponent = ((Element) axiComponent)
449: .getReferent();
450: } else {
451: axiComponent = ((Attribute) axiComponent)
452: .getReferent();
453: }
454: if (axiComponent == null) {
455: setNameInModel(value);
456: return;
457: }
458: ref = getReferenceable(axiComponent);
459: } else {
460: setNameInModel(value);
461: return;
462: }
463: }
464: AXIModel model = getAXIComponent().getModel();
465: // try rename silently
466:
467: try {
468: if (context != null)
469: context.setUserInducedEventMode(true);
470: SchemaModel sm = model.getSchemaModel();
471: // RefactoringManager.getInstance().execute(request, false);
472: SharedUtils.silentRename((Nameable) ref, value, false);
473: model.sync();
474: } catch (CannotRefactorException ex) {
475: SharedUtils.showRenameRefactoringUI((Nameable) ref);
476: // call rename refactoring UI
477: /*
478: WhereUsedView wuv = new WhereUsedView(ref);
479: RenameRefactoringUI ui = new RenameRefactoringUI(wuv, request);
480: TopComponent activetc = TopComponent.getRegistry().getActivated();
481: if (activetc instanceof CloneableEditorSupport.Pane) {
482: new RefactoringPanel(ui, activetc);
483: } else {
484: new RefactoringPanel(ui);
485: }*/
486: } catch (IOException ex) {
487: ErrorManager.getDefault().notify(
488: ErrorManager.INFORMATIONAL, ex);
489: }
490: }
491:
492: public void setNameInModel(String name) {
493: //local component so just rename
494: try {
495: axiComponent.getModel().startTransaction();
496: if (axiComponent instanceof Element)
497: ((Element) axiComponent).setName(name);
498: else if (axiComponent instanceof ContentModel)
499: ((ContentModel) axiComponent).setName(name);
500: else if (axiComponent instanceof Attribute)
501: ((Attribute) axiComponent).setName(name);
502: } finally {
503: axiComponent.getModel().endTransaction();
504: }
505: }
506:
507: public NamedReferenceable getReferenceable() {
508: // if(!getAXIComponent().getOriginal().isGlobal())
509: // return null;
510:
511: SchemaComponent comp = getAXIComponent().getOriginal()
512: .getPeer();
513: if (comp instanceof NamedReferenceable
514: && isValid()
515: && comp.getModel().getModelSource().getLookup().lookup(
516: FileObject.class) != null) {
517: return NamedReferenceable.class.cast(comp);
518: }
519: return null;
520: }
521:
522: private NamedReferenceable getReferenceable(
523: AXIComponent axiComponent) {
524: // if(!axiComponent.getOriginal().isGlobal())
525: // return null;
526: SchemaComponent comp = axiComponent.getOriginal().getPeer();
527: if (comp instanceof NamedReferenceable
528: && isValid()
529: && comp.getModel().getModelSource().getLookup().lookup(
530: FileObject.class) != null) {
531: return NamedReferenceable.class.cast(comp);
532: }
533: return null;
534: }
535:
536: protected boolean isValid() {
537: return getAXIComponent().getPeer().getModel() != null;
538: }
539:
540: private void safeDelete() {
541: final NamedReferenceable ref = getReferenceable();
542: // try delete silently
543: AXIModel model = getAXIComponent().getModel();
544: try {
545: context.setUserInducedEventMode(true);
546: SchemaModel sm = model.getSchemaModel();
547: SharedUtils.silentDeleteRefactor(ref, true);
548: model.sync();
549: } catch (CannotRefactorException ex) {
550: SharedUtils.showDeleteRefactoringUI(ref);
551: // call delete refactoring UI
552:
553: } catch (IOException ex) {
554: ErrorManager.getDefault().notify(
555: ErrorManager.INFORMATIONAL, ex);
556: }
557: }
558:
559: public Sheet.Set getSharedSet(Sheet sheet) {
560: Sheet.Set sharedSet = null;
561: sharedSet = sheet.get("shared");
562: if (sharedSet != null)
563: return sharedSet;
564: sharedSet = sheet.createPropertiesSet();
565: String shared = NbBundle.getMessage(ElementNode.class,
566: "LBL_SHARED");
567: String sharedMessage = NbBundle.getMessage(ElementNode.class,
568: "LBL_SHARED_MESSAGE");
569: sharedSet.setName("shared");//NOI18N
570: sharedSet.setDisplayName(shared);
571: sharedSet.setShortDescription(sharedMessage);
572: return sharedSet;
573: }
574:
575: }
|