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: * If you wish your version of this file to be governed by only the CDDL
025: * or only the GPL Version 2, indicate your decision by adding
026: * "[Contributor] elects to include this software in this distribution
027: * under the [CDDL or GPL Version 2] license." If you do not indicate a
028: * single choice of license, a recipient has the option to distribute
029: * your version of this file under either the CDDL, the GPL Version 2 or
030: * to extend the choice of license to its licensees as provided above.
031: * However, if you add GPL Version 2 code and therefore, elected the GPL
032: * Version 2 license, then the option applies only if the new code is
033: * made subject to such option by the copyright holder.
034: *
035: * Contributor(s):
036: *
037: * Portions Copyrighted 2008 Sun Microsystems, Inc.
038: */
039:
040: package org.netbeans.modules.beans.beaninfo;
041:
042: import java.awt.BorderLayout;
043: import java.awt.EventQueue;
044: import java.awt.Image;
045: import java.beans.BeanInfo;
046: import java.beans.PropertyChangeEvent;
047: import java.beans.PropertyChangeListener;
048: import java.io.Externalizable;
049: import java.io.IOException;
050: import java.io.InputStream;
051: import java.io.ObjectInput;
052: import java.io.ObjectInputStream;
053: import java.io.ObjectOutput;
054: import java.io.ObjectOutputStream;
055: import java.io.OutputStream;
056: import java.io.Reader;
057: import java.io.Serializable;
058: import java.io.Writer;
059: import java.nio.charset.Charset;
060: import java.util.Enumeration;
061: import java.util.Set;
062: import javax.swing.JComponent;
063: import javax.swing.JEditorPane;
064: import javax.swing.JPanel;
065: import javax.swing.text.BadLocationException;
066: import javax.swing.text.Document;
067: import javax.swing.text.EditorKit;
068: import javax.swing.text.StyledDocument;
069: import org.netbeans.api.editor.guards.GuardedSectionManager;
070: import org.netbeans.api.queries.FileEncodingQuery;
071: import org.netbeans.core.spi.multiview.CloseOperationHandler;
072: import org.netbeans.core.spi.multiview.CloseOperationState;
073: import org.netbeans.core.spi.multiview.MultiViewDescription;
074: import org.netbeans.core.spi.multiview.MultiViewElement;
075: import org.netbeans.core.spi.multiview.MultiViewElementCallback;
076: import org.netbeans.core.spi.multiview.MultiViewFactory;
077: import org.netbeans.spi.editor.guards.GuardedEditorSupport;
078: import org.netbeans.spi.editor.guards.GuardedSectionsFactory;
079: import org.netbeans.spi.editor.guards.GuardedSectionsProvider;
080: import org.openide.cookies.EditCookie;
081: import org.openide.cookies.EditorCookie;
082: import org.openide.cookies.OpenCookie;
083: import org.openide.cookies.PrintCookie;
084: import org.openide.cookies.SaveCookie;
085: import org.openide.filesystems.FileLock;
086: import org.openide.filesystems.FileObject;
087: import org.openide.loaders.DataObject;
088: import org.openide.loaders.MultiDataObject;
089: import org.openide.nodes.CookieSet;
090: import org.openide.text.CloneableEditor;
091: import org.openide.text.CloneableEditorSupport;
092: import org.openide.text.CloneableEditorSupport.Pane;
093: import org.openide.text.DataEditorSupport;
094: import org.openide.text.NbDocument;
095: import org.openide.util.HelpCtx;
096: import org.openide.util.NbBundle;
097: import org.openide.util.NbCollections;
098: import org.openide.windows.CloneableOpenSupport;
099: import org.openide.windows.CloneableTopComponent;
100: import org.openide.windows.TopComponent;
101:
102: /**
103: *
104: * @author Jan Pokorsky
105: */
106: final class BIEditorSupport extends DataEditorSupport implements
107: OpenCookie, EditCookie, EditorCookie, PrintCookie,
108: EditorCookie.Observable {
109:
110: private static final String MV_JAVA_ID = "java"; // NOI18N
111: private static final String MV_BEANINFO_ID = "beaninfo"; // NOI18N
112: private BIGES guardedEditor;
113: private GuardedSectionsProvider guardedProvider;
114: private GenerateBeanInfoAction.BeanInfoWorker worker;
115:
116: /**
117: * The embracing multiview TopComponent (holds the form designer and
118: * java editor) - we remeber the last active TopComponent (not all clones)
119: */
120: private CloneableTopComponent multiviewTC;
121: private TopComponentsListener topComponentsListener;
122:
123: public BIEditorSupport(DataObject obj, CookieSet cookieSet) {
124: super (obj, new Environment(obj, cookieSet));
125: setMIMEType("text/x-java"); // NOI18N
126: }
127:
128: public GuardedSectionManager getGuardedSectionManager() {
129: try {
130: StyledDocument doc = openDocument();
131: return GuardedSectionManager.getInstance(doc);
132: } catch (IOException ex) {
133: throw (IllegalStateException) new IllegalStateException(
134: "cannot open document").initCause(ex); // NOI18N
135: }
136: }
137:
138: @Override
139: protected void loadFromStreamToKit(StyledDocument doc,
140: InputStream stream, EditorKit kit) throws IOException,
141: BadLocationException {
142:
143: if (guardedEditor == null) {
144: guardedEditor = new BIGES();
145: GuardedSectionsFactory gFactory = GuardedSectionsFactory
146: .find(((DataEditorSupport.Env) env).getMimeType());
147: if (gFactory != null) {
148: guardedProvider = gFactory.create(guardedEditor);
149: }
150: }
151:
152: if (guardedProvider != null) {
153: guardedEditor.doc = doc;
154: Charset c = FileEncodingQuery.getEncoding(this
155: .getDataObject().getPrimaryFile());
156: Reader reader = guardedProvider.createGuardedReader(stream,
157: c);
158: try {
159: kit.read(reader, doc, 0);
160: } finally {
161: reader.close();
162: }
163: } else {
164: super .loadFromStreamToKit(doc, stream, kit);
165: }
166: }
167:
168: @Override
169: protected void saveFromKitToStream(StyledDocument doc,
170: EditorKit kit, OutputStream stream) throws IOException,
171: BadLocationException {
172:
173: if (guardedProvider != null) {
174: Charset c = FileEncodingQuery.getEncoding(this
175: .getDataObject().getPrimaryFile());
176: Writer writer = guardedProvider.createGuardedWriter(stream,
177: c);
178: try {
179: kit.write(writer, doc, 0, doc.getLength());
180: } finally {
181: writer.close();
182: }
183: } else {
184: super .saveFromKitToStream(doc, kit, stream);
185: }
186: }
187:
188: @Override
189: public void saveDocument() throws IOException {
190: if (worker != null && worker.isModelModified()) {
191: worker.generateSources();
192: worker.waitFinished();
193: }
194: super .saveDocument();
195: }
196:
197: @Override
198: protected boolean notifyModified() {
199: if (!super .notifyModified())
200: return false;
201: ((Environment) this .env).addSaveCookie();
202: updateMVTCName();
203: return true;
204: }
205:
206: @Override
207: protected void notifyUnmodified() {
208: super .notifyUnmodified();
209: ((Environment) this .env).removeSaveCookie();
210: updateMVTCName();
211: }
212:
213: @Override
214: protected void notifyClosed() {
215: super .notifyClosed();
216: worker = null;
217: if (topComponentsListener != null) {
218: TopComponent.getRegistry().removePropertyChangeListener(
219: topComponentsListener);
220: topComponentsListener = null;
221: }
222: }
223:
224: @Override
225: protected Pane createPane() {
226: MultiViewDescription[] descs = { new JavaView(getDataObject()),
227: new BeanInfoView(getDataObject()), };
228: return (Pane) MultiViewFactory.createCloneableMultiView(descs,
229: descs[0], new CloseHandler(getDataObject()));
230: }
231:
232: /** This is called by the multiview elements whenever they are created
233: * (and given a observer knowing their multiview TopComponent). It is
234: * important during deserialization and clonig the multiview - i.e. during
235: * the operations we have no control over. But anytime a multiview is
236: * created, this method gets called.
237: */
238: private void setTopComponent(TopComponent topComp) {
239: multiviewTC = (CloneableTopComponent) topComp;
240: updateMVTCName();
241:
242: if (topComponentsListener == null) {
243: topComponentsListener = new TopComponentsListener();
244: TopComponent.getRegistry().addPropertyChangeListener(
245: topComponentsListener);
246: }
247: }
248:
249: private void updateMVTCName() {
250: Runnable task = new Runnable() {
251: public void run() {
252: updateMVTCNameInAwt();
253: }
254: };
255:
256: if (EventQueue.isDispatchThread()) {
257: task.run();
258: } else {
259: EventQueue.invokeLater(task);
260: }
261: }
262:
263: private void updateMVTCNameInAwt() {
264: CloneableTopComponent topComp = multiviewTC;
265: if (topComp != null) {
266: String htmlname = messageHtmlName();
267: String name = messageName();
268: String tip = messageToolTip();
269: for (CloneableTopComponent o : NbCollections
270: .iterable(topComp.getReference().getComponents())) {
271:
272: topComp.setHtmlDisplayName(htmlname);
273: topComp.setDisplayName(name);
274: topComp.setName(name);
275: topComp.setToolTipText(tip);
276: }
277: }
278: }
279:
280: static boolean isLastView(TopComponent tc) {
281: if (!(tc instanceof CloneableTopComponent))
282: return false;
283:
284: boolean oneOrLess = true;
285: Enumeration en = ((CloneableTopComponent) tc).getReference()
286: .getComponents();
287: if (en.hasMoreElements()) {
288: en.nextElement();
289: if (en.hasMoreElements())
290: oneOrLess = false;
291: }
292: return oneOrLess;
293: }
294:
295: static BIEditorSupport findEditor(DataObject dobj) {
296: return dobj.getLookup().lookup(BIEditorSupport.class);
297: }
298:
299: private static final class CloseHandler implements
300: CloseOperationHandler, Serializable {
301:
302: private static final long serialVersionUID = 1L;
303: private final DataObject dataObject;
304:
305: public CloseHandler(DataObject dataObject) {
306: this .dataObject = dataObject;
307: }
308:
309: public boolean resolveCloseOperation(
310: CloseOperationState[] elements) {
311: BIEditorSupport editor = findEditor(dataObject);
312: return editor != null ? editor.canClose() : true;
313: }
314:
315: }
316:
317: private static final class JavaView implements
318: MultiViewDescription, Serializable {
319:
320: private static final long serialVersionUID = 1L;
321: private static final String FIELD_DATAOBJECT = "dataObject"; // NOI18N
322: private DataObject dataObject;
323:
324: public JavaView(DataObject dataObject) {
325: this .dataObject = dataObject;
326: }
327:
328: public int getPersistenceType() {
329: return TopComponent.PERSISTENCE_ONLY_OPENED;
330: }
331:
332: public String getDisplayName() {
333: return NbBundle.getMessage(JavaView.class,
334: "LAB_JavaSourceView");
335: }
336:
337: public Image getIcon() {
338: return dataObject.getNodeDelegate().getIcon(
339: BeanInfo.ICON_COLOR_16x16);
340: }
341:
342: public HelpCtx getHelpCtx() {
343: return HelpCtx.DEFAULT_HELP;
344: }
345:
346: public String preferredID() {
347: return MV_JAVA_ID;
348: }
349:
350: public MultiViewElement createElement() {
351: BIEditorSupport support = findEditor(dataObject);
352: return new JavaElement(support);
353: }
354:
355: private void writeObject(ObjectOutputStream out)
356: throws IOException {
357: out.putFields().put(FIELD_DATAOBJECT, dataObject);
358: out.writeFields();
359: }
360:
361: private void readObject(ObjectInputStream in)
362: throws IOException, ClassNotFoundException {
363: this .dataObject = (DataObject) in.readFields().get(
364: FIELD_DATAOBJECT, in);
365: }
366: }
367:
368: private static final class BeanInfoView implements
369: MultiViewDescription, Serializable {
370:
371: private static final long serialVersionUID = 1L;
372: private static final String FIELD_DATAOBJECT = "dataObject"; // NOI18N
373: private DataObject dataObject;
374:
375: public BeanInfoView(DataObject dataObject) {
376: this .dataObject = dataObject;
377: }
378:
379: public int getPersistenceType() {
380: return TopComponent.PERSISTENCE_ONLY_OPENED;
381: }
382:
383: public String getDisplayName() {
384: return NbBundle.getMessage(BeanInfoView.class,
385: "LAB_BeanInfoEditorView");
386: }
387:
388: public Image getIcon() {
389: return dataObject.getNodeDelegate().getIcon(
390: BeanInfo.ICON_COLOR_16x16);
391: }
392:
393: public HelpCtx getHelpCtx() {
394: return HelpCtx.DEFAULT_HELP;
395: }
396:
397: public String preferredID() {
398: return MV_BEANINFO_ID;
399: }
400:
401: public MultiViewElement createElement() {
402: return new BeanInfoElement(dataObject);
403: }
404:
405: private void writeObject(ObjectOutputStream out)
406: throws IOException {
407: out.putFields().put(FIELD_DATAOBJECT, dataObject);
408: out.writeFields();
409: }
410:
411: private void readObject(ObjectInputStream in)
412: throws IOException, ClassNotFoundException {
413: this .dataObject = (DataObject) in.readFields().get(
414: FIELD_DATAOBJECT, in);
415: }
416:
417: }
418:
419: private static final class JavaElement extends CloneableEditor
420: implements MultiViewElement, Externalizable {
421:
422: private static final long serialVersionUID = 1L;
423: private MultiViewElementCallback callback;
424:
425: public JavaElement(CloneableEditorSupport support) {
426: super (support);
427: }
428:
429: /**
430: * serialization stuff; do not use
431: */
432: private JavaElement() {
433: }
434:
435: public JComponent getVisualRepresentation() {
436: return this ;
437: }
438:
439: public JComponent getToolbarRepresentation() {
440: JComponent toolbar = null;
441: JEditorPane jepane = getEditorPane();
442: if (jepane != null) {
443: Document doc = jepane.getDocument();
444: if (doc instanceof NbDocument.CustomToolbar) {
445: toolbar = ((NbDocument.CustomToolbar) doc)
446: .createToolbar(jepane);
447: }
448: }
449: return toolbar;
450: }
451:
452: public void setMultiViewCallback(
453: MultiViewElementCallback callback) {
454: this .callback = callback;
455: BIEditorSupport editor = (BIEditorSupport) cloneableEditorSupport();
456: editor.setTopComponent(callback.getTopComponent());
457: }
458:
459: public CloseOperationState canCloseElement() {
460: return isLastView(callback.getTopComponent()) ? MultiViewFactory
461: .createUnsafeCloseState(MV_JAVA_ID,
462: MultiViewFactory.NOOP_CLOSE_ACTION,
463: MultiViewFactory.NOOP_CLOSE_ACTION)
464: : CloseOperationState.STATE_OK;
465: }
466:
467: @Override
468: public void componentActivated() {
469: super .componentActivated();
470: BIEditorSupport editor = (BIEditorSupport) cloneableEditorSupport();
471: if (editor.worker != null
472: && editor.worker.isModelModified()) {
473: editor.worker.generateSources();
474: }
475: }
476:
477: @Override
478: public void componentDeactivated() {
479: super .componentDeactivated();
480: }
481:
482: @Override
483: public void componentHidden() {
484: super .componentHidden();
485: }
486:
487: @Override
488: public void componentShowing() {
489: super .componentShowing();
490: }
491:
492: @Override
493: public void componentClosed() {
494: // XXX copied from form module see issue 55818
495: super .canClose(null, true);
496: super .componentClosed();
497: }
498:
499: @Override
500: protected boolean closeLast() {
501: return true;
502: }
503:
504: @Override
505: public void componentOpened() {
506: super .componentOpened();
507: }
508:
509: @Override
510: public void updateName() {
511: super .updateName();
512: if (callback != null) {
513: callback.updateTitle(getDisplayName());
514: }
515: }
516:
517: @Override
518: public void readExternal(ObjectInput in) throws IOException,
519: ClassNotFoundException {
520: super .readExternal(in);
521: }
522:
523: @Override
524: public void writeExternal(ObjectOutput out) throws IOException {
525: super .writeExternal(out);
526: }
527:
528: }
529:
530: private static final class BeanInfoElement extends
531: CloneableTopComponent implements MultiViewElement,
532: Externalizable {
533:
534: private static final long serialVersionUID = 1L;
535: private MultiViewElementCallback callback;
536: private DataObject dataObject;
537: private boolean isInitialized = false;
538: private final JPanel emptyToolbar = new JPanel();
539: private BiPanel biPanel;
540:
541: public BeanInfoElement(DataObject dataObject) {
542: this .dataObject = dataObject;
543: }
544:
545: /**
546: * serialization stuff; do not use
547: */
548: private BeanInfoElement() {
549: }
550:
551: public JComponent getVisualRepresentation() {
552: return this ;
553: }
554:
555: public JComponent getToolbarRepresentation() {
556: return emptyToolbar;
557: }
558:
559: public void setMultiViewCallback(
560: MultiViewElementCallback callback) {
561: this .callback = callback;
562: BIEditorSupport editor = findEditor(dataObject);
563: editor.setTopComponent(callback.getTopComponent());
564: }
565:
566: public CloseOperationState canCloseElement() {
567: return isLastView(callback.getTopComponent()) ? MultiViewFactory
568: .createUnsafeCloseState(MV_JAVA_ID,
569: MultiViewFactory.NOOP_CLOSE_ACTION,
570: MultiViewFactory.NOOP_CLOSE_ACTION)
571: : CloseOperationState.STATE_OK;
572: }
573:
574: @Override
575: public void componentActivated() {
576: super .componentActivated();
577: }
578:
579: @Override
580: public void componentDeactivated() {
581: super .componentDeactivated();
582: }
583:
584: @Override
585: public void componentHidden() {
586: super .componentHidden();
587: }
588:
589: @Override
590: public void componentShowing() {
591: super .componentShowing();
592: initialize();
593: }
594:
595: @Override
596: public void componentClosed() {
597: super .componentClosed();
598: }
599:
600: @Override
601: public void componentOpened() {
602: super .componentOpened();
603: }
604:
605: private void initialize() {
606: if (!isInitialized) {
607: setLayout(new BorderLayout());
608: biPanel = new BiPanel();
609: add(biPanel, BorderLayout.CENTER);
610: isInitialized = true;
611: }
612:
613: FileObject biFile = dataObject.getPrimaryFile();
614: String name = biFile.getName();
615: name = name.substring(0, name.length()
616: - "BeanInfo".length()); // NOI18N
617: FileObject javaFile = biFile.getParent().getFileObject(
618: name, biFile.getExt());
619: BIEditorSupport editor = findEditor(dataObject);
620: if (javaFile != null) {
621: editor.worker = new GenerateBeanInfoAction.BeanInfoWorker(
622: javaFile, biPanel);
623: editor.worker.analyzePatterns();
624: editor.worker.updateUI();
625: } else {
626: // notify missing source file
627: biPanel.setContext(BiNode.createNoSourceNode(biFile));
628: }
629: }
630:
631: @Override
632: public void readExternal(ObjectInput oi) throws IOException,
633: ClassNotFoundException {
634: super .readExternal(oi);
635: dataObject = (DataObject) oi.readObject();
636: // initialize();
637: }
638:
639: @Override
640: public void writeExternal(ObjectOutput oo) throws IOException {
641: super .writeExternal(oo);
642: oo.writeObject(dataObject);
643: }
644:
645: }
646:
647: private static final class BIGES implements GuardedEditorSupport {
648:
649: StyledDocument doc = null;
650:
651: public StyledDocument getDocument() {
652: return BIGES.this .doc;
653: }
654: }
655:
656: private static final class Environment extends
657: DataEditorSupport.Env {
658:
659: private static final long serialVersionUID = -1;
660:
661: private final transient CookieSet cookieSet;
662: private transient SaveSupport saveCookie = null;
663:
664: private final class SaveSupport implements SaveCookie {
665: public void save() throws java.io.IOException {
666: DataObject dobj = getDataObject();
667: ((DataEditorSupport) findCloneableOpenSupport())
668: .saveDocument();
669: dobj.setModified(false);
670: }
671: }
672:
673: public Environment(DataObject obj, CookieSet cookieSet) {
674: super (obj);
675: this .cookieSet = cookieSet;
676: }
677:
678: protected FileObject getFile() {
679: return this .getDataObject().getPrimaryFile();
680: }
681:
682: protected FileLock takeLock() throws java.io.IOException {
683: return ((MultiDataObject) this .getDataObject())
684: .getPrimaryEntry().takeLock();
685: }
686:
687: public @Override
688: CloneableOpenSupport findCloneableOpenSupport() {
689: return findEditor(this .getDataObject());
690: }
691:
692: public void addSaveCookie() {
693: DataObject javaData = this .getDataObject();
694: if (javaData.getCookie(SaveCookie.class) == null) {
695: if (this .saveCookie == null)
696: this .saveCookie = new SaveSupport();
697: this .cookieSet.add(this .saveCookie);
698: javaData.setModified(true);
699: }
700: }
701:
702: public void removeSaveCookie() {
703: DataObject javaData = this .getDataObject();
704: if (javaData.getCookie(SaveCookie.class) != null) {
705: this .cookieSet.remove(this .saveCookie);
706: javaData.setModified(false);
707: }
708: }
709: }
710:
711: private class TopComponentsListener implements
712: PropertyChangeListener {
713:
714: public void propertyChange(PropertyChangeEvent evt) {
715: if (TopComponent.Registry.PROP_OPENED.equals(evt
716: .getPropertyName())) {
717: // Check closed top components
718: @SuppressWarnings("unchecked")
719: Set<TopComponent> closed = (Set<TopComponent>) evt
720: .getOldValue();
721: closed.removeAll((Set) evt.getNewValue());
722: for (TopComponent o : closed) {
723: if (o instanceof CloneableTopComponent) {
724: final CloneableTopComponent topComponent = (CloneableTopComponent) o;
725: Enumeration en = topComponent.getReference()
726: .getComponents();
727: if (multiviewTC == topComponent) {
728: if (en.hasMoreElements()) {
729: // Remember next cloned top component
730: multiviewTC = (CloneableTopComponent) en
731: .nextElement();
732: } else {
733: // All cloned top components are closed
734: notifyClosed();
735: }
736: }
737: }
738: }
739: }
740: }
741:
742: }
743:
744: }
|