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.xam.ui.customizer;
043:
044: import java.awt.BorderLayout;
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047: import java.io.IOException;
048: import java.net.URISyntaxException;
049: import java.util.ArrayList;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.Set;
053: import javax.swing.ImageIcon;
054: import javax.swing.event.DocumentEvent;
055: import javax.swing.event.DocumentListener;
056: import javax.swing.tree.TreeSelectionModel;
057: import org.netbeans.api.project.FileOwnerQuery;
058: import org.netbeans.api.project.Project;
059: import org.netbeans.modules.xml.catalogsupport.DefaultProjectCatalogSupport;
060: import org.netbeans.modules.xml.xam.locator.CatalogModelException;
061: import org.netbeans.spi.project.ui.LogicalViewProvider;
062: import org.openide.explorer.view.BeanTreeView;
063: import org.netbeans.modules.xml.xam.Component;
064: import org.netbeans.modules.xml.xam.Model;
065: import org.openide.explorer.ExplorerManager;
066: import org.openide.filesystems.FileObject;
067: import org.openide.nodes.AbstractNode;
068: import org.openide.nodes.Children;
069: import org.openide.nodes.Node;
070: import org.openide.util.NbBundle;
071: import org.openide.util.Utilities;
072:
073: /**
074: * Base class for external reference customizers.
075: *
076: * @author Ajit Bhate
077: * @author Nathan Fiedler
078: */
079: public abstract class ExternalReferenceCustomizer<T extends Component>
080: extends AbstractReferenceCustomizer<T> implements
081: DocumentListener, ExplorerManager.Provider,
082: PropertyChangeListener {
083: /** silence compiler warnings */
084: private static final long serialVersionUID = 1L;
085: /** If true, the prefix was generated and not edited by the user. */
086: private transient boolean prefixGenerated;
087: /** The file being modified (where the import will be added). */
088: private transient FileObject sourceFO;
089: /** The file selected by the user. */
090: private transient FileObject referencedFO;
091: /** Used to deal with project catalogs. */
092: private transient DefaultProjectCatalogSupport catalogSupport;
093:
094: /**
095: * Creates new form ExternalReferenceCustomizer
096: *
097: * @param component external reference to customize.
098: */
099: public ExternalReferenceCustomizer(T component, Model model) {
100: super (component);
101: initComponents();
102: sourceFO = (FileObject) component.getModel().getModelSource()
103: .getLookup().lookup(FileObject.class);
104: catalogSupport = DefaultProjectCatalogSupport
105: .getInstance(sourceFO);
106: init(component, model);
107: initializeUI();
108: // View for selecting an external reference.
109: BeanTreeView locationView = new BeanTreeView();
110: locationView.setPopupAllowed(false);
111: locationView.setDefaultActionAllowed(false);
112: locationView
113: .setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
114: locationView.setRootVisible(false);
115: locationView.getAccessibleContext().setAccessibleName(
116: locationLabel.getToolTipText());
117: locationView.getAccessibleContext().setAccessibleDescription(
118: locationLabel.getToolTipText());
119: locationPanel.add(locationView, BorderLayout.CENTER);
120: explorerManager = new ExplorerManager();
121: explorerManager.addPropertyChangeListener(this );
122: explorerManager.setRootContext(createRootNode());
123: }
124:
125: @Override
126: public void addNotify() {
127: super .addNotify();
128: // Force the Ok button to be disabled initially.
129: firePropertyChange(PROP_ACTION_APPLY, true, false);
130: }
131:
132: public void applyChanges() throws IOException {
133: if (mustNamespaceDiffer() && isPrefixChanged()) {
134: prefixTextField.setEditable(false);
135: }
136: if (isLocationChanged()
137: && referencedFO != null
138: && catalogSupport.needsCatalogEntry(sourceFO,
139: referencedFO)) {
140: try {
141: catalogSupport.createCatalogEntry(sourceFO,
142: referencedFO);
143: } catch (IOException ioe) {
144: } catch (CatalogModelException cme) {
145: }
146: }
147: }
148:
149: /**
150: * Retrieves the location value from the interface.
151: *
152: * @return new location value.
153: */
154: protected String getEditedLocation() {
155: if (referencedFO == null) {
156: return getReferenceLocation();
157: }
158: try {
159: return catalogSupport.getReferenceURI(sourceFO,
160: referencedFO).toString();
161: } catch (URISyntaxException ex) {
162: }
163: return null;
164: }
165:
166: /**
167: * Retrieves the namespace value from the interface.
168: *
169: * @return new namespace value, sans leading and trailing whitespace.
170: */
171: protected String getEditedNamespace() {
172: return namespaceTextField.getText().trim();
173: }
174:
175: /**
176: * Retrieves the prefix value from the interface.
177: *
178: * @return new prefix value, sans leading and trailing whitespace.
179: */
180: protected String getEditedPrefix() {
181: return prefixTextField.getText().trim();
182: }
183:
184: /**
185: * Returns the location value from the original component.
186: *
187: * @return original location value.
188: */
189: protected abstract String getReferenceLocation();
190:
191: /**
192: * Returns the namespace value from the original component.
193: *
194: * @return original namespace value.
195: */
196: protected abstract String getNamespace();
197:
198: /**
199: * Returns the prefix value from the original component.
200: *
201: * @return original prefix value.
202: */
203: protected abstract String getPrefix();
204:
205: /**
206: * Generate a unique prefix value (e.g. "ns1") for the component.
207: *
208: * @return unique prefix value.
209: */
210: protected abstract String generatePrefix();
211:
212: /**
213: * Indicates if the location value was changed in the interface.
214: *
215: * @return true if location was changed.
216: */
217: protected boolean isLocationChanged() {
218: String rl = getReferenceLocation();
219: String el = getEditedLocation();
220: if (rl == null) {
221: return el != null;
222: }
223: return !rl.equals(el);
224: }
225:
226: /**
227: * Indicates if the namespace value was changed in the interface.
228: *
229: * @return true if namespace was changed.
230: */
231: protected boolean isNamespaceChanged() {
232: if (!mustNamespaceDiffer()) {
233: return false;
234: }
235: String ns = getNamespace();
236: String ens = getEditedNamespace();
237: if (ns == null) {
238: return ens.length() > 0;
239: }
240: return !ns.equals(ens);
241: }
242:
243: /**
244: * Indicates if the prefix value was changed in the interface.
245: *
246: * @return true if prefix was changed.
247: */
248: protected boolean isPrefixChanged() {
249: if (!mustNamespaceDiffer()) {
250: return false;
251: }
252: if (prefixGenerated) {
253: // User has not yet modified the prefix value.
254: return false;
255: }
256: String p = getPrefix();
257: String ep = getEditedPrefix();
258: if (p == null) {
259: return ep.length() > 0;
260: }
261: return !p.equals(ep);
262: }
263:
264: /**
265: * Called from constructor, after the interface components have been
266: * constructed, but before they have been initialized. Gives subclasses
267: * a chance to perform initialization based on the given component.
268: *
269: * @param component the reference to be customized.
270: * @param model the model passed to the constructor (may be null).
271: */
272: protected void init(T component, Model model) {
273: // Note, do not place any code here, as there is no guarantee
274: // that the subclasses will delegate to this method at all.
275: }
276:
277: public void changedUpdate(DocumentEvent e) {
278: }
279:
280: public void insertUpdate(DocumentEvent e) {
281: prefixGenerated = false;
282: validateInput();
283: }
284:
285: public void removeUpdate(DocumentEvent e) {
286: prefixGenerated = false;
287: validateInput();
288: }
289:
290: protected void initializeUI() {
291: // TODO select in panel
292: if (mustNamespaceDiffer()) {
293: namespaceTextField.setText(getNamespace());
294: prefixTextField.getDocument().removeDocumentListener(this );
295: String prefix = getPrefix();
296: if (prefix != null) {
297: prefixTextField.setText(prefix);
298: prefixTextField.setEditable(false);
299: prefixGenerated = false;
300: } else {
301: prefix = generatePrefix();
302: prefixGenerated = true;
303: prefixTextField.setText(prefix);
304: prefixTextField.getDocument().addDocumentListener(this );
305: }
306: } else {
307: namespaceLabel.setVisible(false);
308: namespaceTextField.setVisible(false);
309: prefixLabel.setVisible(false);
310: prefixTextField.setVisible(false);
311: }
312: }
313:
314: public ExternalReferenceDataNode createExternalReferenceNode(
315: Node original) {
316: return new ExternalReferenceDataNode(original,
317: getNodeDecorator());
318: }
319:
320: /**
321: * Determine if the user's input is valid or not. This will enable
322: * or disable the save/reset controls based on the results, as well
323: * as issue error messages.
324: */
325: private void validateInput() {
326: boolean lChanged = isLocationChanged();
327: boolean nsChanged = isNamespaceChanged();
328: boolean pChanged = isPrefixChanged();
329: if (!lChanged && !nsChanged && !pChanged) {
330: setSaveEnabled(false);
331: setResetEnabled(false);
332: } else {
333: setResetEnabled(true);
334: String msg = null;
335: if (mustNamespaceDiffer()) {
336: if (lChanged && getEditedLocation() == null) {
337: msg = NbBundle
338: .getMessage(
339: ExternalReferenceCustomizer.class,
340: "LBL_ExternalReferenceCustomizer_InvalidLocation");
341: }
342: Map<String, String> prefixMap = getPrefixes(getModelComponent()
343: .getModel());
344: String ep = getEditedPrefix();
345: if (pChanged
346: && (ep.length() == 0 || prefixMap
347: .containsKey(ep))) {
348: msg = NbBundle
349: .getMessage(
350: ExternalReferenceCustomizer.class,
351: "LBL_ExternalReferenceCustomizer_InvalidPrefix");
352: }
353: }
354: // Changing the location should allow the prefix to change.
355: prefixTextField.setEditable(lChanged);
356: if (msg != null) {
357: showMessage(msg);
358: }
359: setSaveEnabled(msg == null);
360: }
361: }
362:
363: protected void showMessage(String msg) {
364: if (msg == null) {
365: messageLabel.setText(" ");
366: messageLabel.setIcon(null);
367: } else {
368: messageLabel.setText(msg);
369: messageLabel
370: .setIcon(new ImageIcon(
371: Utilities
372: .loadImage("org/netbeans/modules/xml/xam/ui/resources/error.gif"))); // NOI18N
373: }
374: }
375:
376: protected Node createRootNode() {
377: Set/*<Project>*/refProjects = null;
378: if (catalogSupport.supportsCrossProject()) {
379: refProjects = catalogSupport.getProjectReferences();
380: }
381: ExternalReferenceDecorator decorator = getNodeDecorator();
382: Node[] rootNodes = new Node[1 + (refProjects == null ? 0
383: : refProjects.size())];
384: Project prj = FileOwnerQuery.getOwner(sourceFO);
385: LogicalViewProvider viewProvider = (LogicalViewProvider) prj
386: .getLookup().lookup(LogicalViewProvider.class);
387: rootNodes[0] = decorator
388: .createExternalReferenceNode(viewProvider
389: .createLogicalView());
390: int rootIndex = 1;
391: List<FileObject> projectRoots = new ArrayList<FileObject>();
392: projectRoots.add(prj.getProjectDirectory());
393: if (refProjects != null) {
394: for (Object o : refProjects) {
395: Project refPrj = (Project) o;
396: viewProvider = (LogicalViewProvider) refPrj.getLookup()
397: .lookup(LogicalViewProvider.class);
398: rootNodes[rootIndex++] = decorator
399: .createExternalReferenceNode(viewProvider
400: .createLogicalView());
401: projectRoots.add(refPrj.getProjectDirectory());
402: }
403: }
404: FileObject[] roots = projectRoots
405: .toArray(new FileObject[projectRoots.size()]);
406: Children fileChildren = new Children.Array();
407: fileChildren.add(rootNodes);
408: Node byFilesNode = new FolderNode(fileChildren);
409: byFilesNode.setDisplayName(NbBundle.getMessage(
410: ExternalReferenceCustomizer.class,
411: "LBL_ExternalReferenceCustomizer_Category_By_File"));
412:
413: // Construct the By Namespace node.
414: Children nsChildren = new NamespaceChildren(roots, decorator);
415: Node byNSNode = new FolderNode(nsChildren);
416: byNSNode
417: .setDisplayName(NbBundle
418: .getMessage(ExternalReferenceCustomizer.class,
419: "LBL_ExternalReferenceCustomizer_Category_By_Namespace"));
420: // Hide the Retrieved node tree until we are sure the runtime can handle
421: // URLs with respect to the catalog.
422: // Node retrievedNode;
423: // CatalogWriteModel cwm = getCatalogWriteModel();
424: // if (cwm != null) {
425: // Children rChildren = new RetrievedFilesChildren(cwm , decorator);
426: // retrievedNode = new ExternalReferenceNode(projectNode, rChildren, decorator);
427: // } else {
428: // retrievedNode = new ExternalReferenceNode(projectNode, Children.LEAF, decorator);
429: // }
430: // retrievedNode.setDisplayName(NbBundle.getMessage(
431: // ExternalReferenceCustomizer.class,
432: // "LBL_ExternalReferenceCustomizer_Category_By_Retrieved"));
433: Children categories = new Children.Array();
434: // categories.add(new Node[]{ byFilesNode, byNSNode, retrievedNode });
435: categories.add(new Node[] { byFilesNode, byNSNode });
436: return new AbstractNode(categories);
437: }
438:
439: // private CatalogWriteModel getCatalogWriteModel() {
440: // try {
441: // FileObject myFobj = (FileObject) getModelComponent().getModel().
442: // getModelSource().getLookup().lookup(FileObject.class);
443: // CatalogWriteModel cwm = CatalogWriteModelFactory.getInstance().
444: // getCatalogWriteModelForProject(myFobj);
445: // return cwm;
446: // } catch (CatalogModelException cme) {
447: // }
448: // return null;
449: // }
450:
451: public void propertyChange(PropertyChangeEvent event) {
452: if (ExplorerManager.PROP_SELECTED_NODES.equals(event
453: .getPropertyName())) {
454: // Reset everything to assume an invalid selection.
455: showMessage(null);
456: setSaveEnabled(false);
457: String ns = null;
458: referencedFO = null;
459: Node[] nodes = (Node[]) event.getNewValue();
460: // Validate the node selection.
461: if (nodes != null && nodes.length > 0
462: && nodes[0] instanceof ExternalReferenceNode) {
463: ExternalReferenceNode node = (ExternalReferenceNode) nodes[0];
464: Model model = node.getModel();
465: // Without a model, the selection is completely invalid.
466: if (model != null) {
467: ns = getTargetNamespace(model);
468: if (model != getModelComponent().getModel()) {
469: referencedFO = (FileObject) model
470: .getModelSource().getLookup().lookup(
471: FileObject.class);
472: }
473: // Ask decorator if selection is valid or not.
474: String msg = getNodeDecorator().validate(node);
475: if (msg != null) {
476: showMessage(msg);
477: } else {
478: // If node is okay, validate the rest of the input.
479: validateInput();
480: }
481: }
482: }
483: namespaceTextField.setText(ns);
484: }
485: }
486:
487: public ExplorerManager getExplorerManager() {
488: return explorerManager;
489: }
490:
491: /**
492: * This method is called from within the constructor to
493: * initializeTypeView the form.
494: * WARNING: Do NOT modify this code. The content of this method is
495: * always regenerated by the Form Editor.
496: */
497: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
498: private void initComponents() {
499: locationLabel = new javax.swing.JLabel();
500: locationPanel = new javax.swing.JPanel();
501: namespaceLabel = new javax.swing.JLabel();
502: namespaceTextField = new javax.swing.JTextField();
503: prefixLabel = new javax.swing.JLabel();
504: prefixTextField = new javax.swing.JTextField();
505: messageLabel = new javax.swing.JLabel();
506:
507: locationLabel.setLabelFor(locationPanel);
508: org.openide.awt.Mnemonics
509: .setLocalizedText(
510: locationLabel,
511: java.util.ResourceBundle
512: .getBundle(
513: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
514: .getString(
515: "LBL_ExternalReferenceCustomizer_Location"));
516: locationLabel
517: .setToolTipText(java.util.ResourceBundle
518: .getBundle(
519: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
520: .getString(
521: "TIP_ExternalReferenceCustomizer_Location"));
522:
523: locationPanel.setLayout(new java.awt.BorderLayout());
524:
525: locationPanel.setBorder(javax.swing.BorderFactory
526: .createEtchedBorder());
527:
528: namespaceLabel.setLabelFor(namespaceTextField);
529: org.openide.awt.Mnemonics
530: .setLocalizedText(
531: namespaceLabel,
532: java.util.ResourceBundle
533: .getBundle(
534: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
535: .getString(
536: "LBL_ExternalReferenceCustomizer_Namespace"));
537: namespaceLabel
538: .setToolTipText(java.util.ResourceBundle
539: .getBundle(
540: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
541: .getString(
542: "TIP_ExternalReferenceCustomizer_Namespace"));
543:
544: namespaceTextField.setEditable(false);
545:
546: prefixLabel.setLabelFor(prefixTextField);
547: org.openide.awt.Mnemonics
548: .setLocalizedText(
549: prefixLabel,
550: java.util.ResourceBundle
551: .getBundle(
552: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
553: .getString(
554: "LBL_ExternalReferenceCustomizer_Prefix"));
555: prefixLabel.setToolTipText(java.util.ResourceBundle.getBundle(
556: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
557: .getString("TIP_ExternalReferenceCustomizer_Prefix"));
558:
559: prefixTextField
560: .setToolTipText(java.util.ResourceBundle
561: .getBundle(
562: "org/netbeans/modules/xml/xam/ui/customizer/Bundle")
563: .getString(
564: "TIP_ExternalReferenceCustomizer_Prefix"));
565:
566: messageLabel.setForeground(new java.awt.Color(255, 0, 0));
567: org.openide.awt.Mnemonics.setLocalizedText(messageLabel, " ");
568:
569: org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(
570: this );
571: this .setLayout(layout);
572: layout
573: .setHorizontalGroup(layout
574: .createParallelGroup(
575: org.jdesktop.layout.GroupLayout.LEADING)
576: .add(
577: layout
578: .createSequentialGroup()
579: .addContainerGap()
580: .add(
581: layout
582: .createParallelGroup(
583: org.jdesktop.layout.GroupLayout.LEADING)
584: .add(
585: messageLabel,
586: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
587: 382,
588: Short.MAX_VALUE)
589: .add(
590: locationPanel,
591: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
592: 382,
593: Short.MAX_VALUE)
594: .add(
595: org.jdesktop.layout.GroupLayout.TRAILING,
596: layout
597: .createSequentialGroup()
598: .add(
599: layout
600: .createParallelGroup(
601: org.jdesktop.layout.GroupLayout.LEADING)
602: .add(
603: namespaceLabel)
604: .add(
605: prefixLabel))
606: .addPreferredGap(
607: org.jdesktop.layout.LayoutStyle.RELATED)
608: .add(
609: layout
610: .createParallelGroup(
611: org.jdesktop.layout.GroupLayout.LEADING)
612: .add(
613: prefixTextField,
614: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
615: 297,
616: Short.MAX_VALUE)
617: .add(
618: namespaceTextField,
619: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
620: 297,
621: Short.MAX_VALUE)))
622: .add(
623: locationLabel))
624: .addContainerGap()));
625: layout
626: .setVerticalGroup(layout
627: .createParallelGroup(
628: org.jdesktop.layout.GroupLayout.LEADING)
629: .add(
630: org.jdesktop.layout.GroupLayout.TRAILING,
631: layout
632: .createSequentialGroup()
633: .addContainerGap()
634: .add(locationLabel)
635: .addPreferredGap(
636: org.jdesktop.layout.LayoutStyle.RELATED)
637: .add(
638: locationPanel,
639: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
640: 330, Short.MAX_VALUE)
641: .addPreferredGap(
642: org.jdesktop.layout.LayoutStyle.RELATED)
643: .add(
644: layout
645: .createParallelGroup(
646: org.jdesktop.layout.GroupLayout.TRAILING)
647: .add(
648: namespaceLabel)
649: .add(
650: namespaceTextField,
651: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
652: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
653: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
654: .add(12, 12, 12)
655: .add(
656: layout
657: .createParallelGroup(
658: org.jdesktop.layout.GroupLayout.BASELINE)
659: .add(
660: prefixLabel)
661: .add(
662: prefixTextField,
663: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
664: org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
665: org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
666: .addPreferredGap(
667: org.jdesktop.layout.LayoutStyle.RELATED)
668: .add(messageLabel)
669: .addContainerGap()));
670: }// </editor-fold>//GEN-END:initComponents
671:
672: // Variables declaration - do not modify//GEN-BEGIN:variables
673: public javax.swing.JLabel locationLabel;
674: public javax.swing.JPanel locationPanel;
675: public javax.swing.JLabel messageLabel;
676: public javax.swing.JLabel namespaceLabel;
677: public javax.swing.JTextField namespaceTextField;
678: public javax.swing.JLabel prefixLabel;
679: public javax.swing.JTextField prefixTextField;
680: // End of variables declaration//GEN-END:variables
681: }
|