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-2006 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: package org.netbeans.modules.vmd.api.io.providers;
042:
043: import org.netbeans.modules.vmd.api.io.DataObjectContext;
044: import org.netbeans.modules.vmd.api.io.DesignDocumentAwareness;
045: import org.netbeans.modules.vmd.api.model.DesignDocument;
046: import org.netbeans.modules.vmd.api.model.DocumentInterface;
047: import org.netbeans.modules.vmd.io.DocumentInterfaceImpl;
048: import org.netbeans.modules.vmd.io.DocumentLoad;
049: import org.netbeans.modules.vmd.io.DocumentSave;
050: import org.openide.ErrorManager;
051: import org.openide.awt.UndoRedo;
052: import org.openide.util.RequestProcessor;
053:
054: import java.util.concurrent.CopyOnWriteArrayList;
055: import java.util.ArrayList;
056: import java.lang.ref.WeakReference;
057: import org.netbeans.modules.vmd.api.io.serialization.DocumentErrorHandler;
058: import org.netbeans.modules.vmd.api.io.serialization.DocumentErrorHandlerSupport;
059:
060: /**
061: * @author David Kaspar
062: */
063: // TODO - versioning + plugable serializers
064: // TODO - memory leak - listeners has to be weak references.
065: public final class DocumentSerializer {
066:
067: private CopyOnWriteArrayList<DesignDocumentAwareness> listeners;
068: private DataObjectContext context;
069: private DesignDocument document;
070: private UndoRedo.Manager undoRedoManager;
071: private ArrayList<WeakReference<DocumentInterface>> documentInterfaces = new ArrayList<WeakReference<DocumentInterface>>();
072:
073: private volatile boolean loaded = false;
074: private volatile boolean loading = false;
075:
076: private Runnable loader = new Runnable() {
077: public void run() {
078: DataObjectContext context = DocumentSerializer.this .context;
079: if (context == null)
080: return;
081:
082: undoRedoManager.discardAllEdits();
083: DocumentInterfaceImpl loadingDocumentInterface = new DocumentInterfaceImpl(
084: context, undoRedoManager);
085: documentInterfaces
086: .add(new WeakReference<DocumentInterface>(
087: loadingDocumentInterface));
088: final DesignDocument loadingDocument = new DesignDocument(
089: loadingDocumentInterface);
090: DocumentErrorHandler errorHandler = new DocumentErrorHandler();
091: DocumentLoad.load(context, loadingDocument, errorHandler);
092: if (!errorHandler.getErrors().isEmpty()) {
093: DocumentErrorHandlerSupport
094: .showDocumentErrorHandlerDialog(errorHandler,
095: context.getDataObject()
096: .getPrimaryFile());
097: IOSupport.getCloneableEditorSupport(
098: context.getDataObject()).close();
099: return;
100: } else if (!errorHandler.getWarnings().isEmpty())
101: DocumentErrorHandlerSupport
102: .showDocumentErrorHandlerDialog(errorHandler,
103: context.getDataObject()
104: .getPrimaryFile());
105:
106: IOSupport.resetCodeResolver(context.getDataObject(),
107: loadingDocument); // HINT - if a new document is created which should update source code then do not call this method
108: loadingDocumentInterface.enable();
109: synchronized (DocumentSerializer.this ) {
110: if (DocumentSerializer.this .context == null) // document has been closed during loading, issue #120096
111: return;
112: document = loadingDocument;
113: loaded = true;
114: loading = false;
115: DocumentSerializer.this .notifyAll();
116: }
117:
118: // TODO - possible race condition with notifyDataObjectClosed method
119: fireDesignDocumentAwareness(DocumentSerializer.this .document);
120: }
121: };
122:
123: DocumentSerializer(DataObjectContext context) {
124: this .context = context;
125: listeners = new CopyOnWriteArrayList<DesignDocumentAwareness>();
126: undoRedoManager = new UndoRedo.Manager();
127: }
128:
129: public DesignDocument getDocument() {
130: startLoadingDocument();
131: return getActualDocument();
132: }
133:
134: public DesignDocument getActualDocument() {
135: synchronized (this ) {
136: return document;
137: }
138: }
139:
140: public boolean isLoadingOrLoaded() {
141: synchronized (this ) {
142: return loading || loaded;
143: }
144: }
145:
146: public void startLoadingDocument() {
147: synchronized (this ) {
148: if (loaded || loading)
149: return;
150: loading = true;
151:
152: RequestProcessor.getDefault().post(loader);
153: }
154: }
155:
156: public void restartLoadingDocument() {
157: synchronized (this ) {
158: loaded = false;
159: startLoadingDocument();
160: }
161: }
162:
163: public void waitDocumentLoaded() {
164: startLoadingDocument();
165: try {
166: synchronized (this ) {
167: if (loaded)
168: return;
169: if (loading)
170: wait();
171: }
172: } catch (InterruptedException e) {
173: ErrorManager.getDefault().notify(e);
174: }
175: }
176:
177: // public void reloadDocument () {
178: // try {
179: // synchronized (this) {
180: // if (loading)
181: // wait ();
182: // loaded = false;
183: // }
184: // } catch (InterruptedException e) {
185: // ErrorManager.getDefault ().notify (e);
186: // }
187: // waitDocumentLoaded ();
188: // }
189:
190: public void saveDocument() {
191: waitDocumentLoaded();
192: final DesignDocument savingDocument;
193: synchronized (this ) {
194: savingDocument = document;
195: }
196: savingDocument.getTransactionManager().readAccess(
197: new Runnable() {
198: public void run() {
199: DocumentSave.save(context, savingDocument);
200: }
201: });
202: }
203:
204: public void addDesignDocumentAwareness(
205: DesignDocumentAwareness listener) {
206: listeners.add(listener);
207: listener.setDesignDocument(getActualDocument());
208: }
209:
210: public void removeDesignDocumentAwareness(
211: DesignDocumentAwareness listener) {
212: listeners.remove(listener);
213: }
214:
215: private void fireDesignDocumentAwareness(DesignDocument newDocument) {
216: for (DesignDocumentAwareness listener : listeners)
217: listener.setDesignDocument(newDocument);
218: }
219:
220: public UndoRedo.Manager getUndoRedoManager() {
221: synchronized (this ) {
222: return undoRedoManager;
223: }
224: }
225:
226: public String getProjectType() {
227: return DocumentLoad.loadProjectType(context);
228: }
229:
230: // TODO - possible race condition - when a data object is closed while a related document is still loading, so the document could be set after that
231: void notifyDataObjectClosed() {
232: synchronized (this ) {
233: document = null;
234: undoRedoManager = null;
235: context = null;
236: }
237: fireDesignDocumentAwareness(null);
238: listeners.clear();
239: }
240:
241: boolean hasDocumentInterface(DocumentInterface documentInterface) {
242: if (documentInterface == null)
243: return false;
244: for (WeakReference<DocumentInterface> ref : documentInterfaces)
245: if (ref.get() == documentInterface)
246: return true;
247: return false;
248: }
249:
250: }
|