KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > multiview > XmlMultiViewDataObject


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.multiview;
21
22 import org.netbeans.core.spi.multiview.MultiViewElement;
23 import org.openide.cookies.SaveCookie;
24 import org.openide.cookies.EditorCookie;
25 import org.openide.filesystems.FileObject;
26 import org.openide.filesystems.FileLock;
27 import org.openide.filesystems.FileAlreadyLockedException;
28 import org.openide.loaders.DataObjectExistsException;
29 import org.openide.loaders.MultiDataObject;
30 import org.openide.loaders.MultiFileLoader;
31 import org.openide.nodes.CookieSet;
32 import org.openide.windows.CloneableTopComponent;
33 import org.openide.ErrorManager;
34 import org.openide.NotifyDescriptor;
35 import org.openide.DialogDisplayer;
36 import org.openide.util.io.ReaderInputStream;
37 import org.openide.util.NbBundle;
38
39 import java.io.*;
40 import java.util.Enumeration JavaDoc;
41 import java.util.Date JavaDoc;
42 import java.lang.ref.WeakReference JavaDoc;
43
44 /**
45  * Base class for data objects that are used as a basis for
46  * the xml multiview. Provides support for caching data ({@link DataCache}), switching
47  * view, encoding and keeping track of currently active multiview element. Furthermore, it
48  * associates <code>XmlMultiViewEditorSupport</code> with this data object.
49  *
50  * Created on October 5, 2004, 10:49 AM
51  * @author mkuchtiak
52  */

53 public abstract class XmlMultiViewDataObject extends MultiDataObject implements CookieSet.Factory {
54     
55     public static final String JavaDoc PROP_DOCUMENT_VALID = "document_valid"; //NOI18N
56
public static final String JavaDoc PROP_SAX_ERROR = "sax_error"; //NOI18N
57
public static final String JavaDoc PROPERTY_DATA_MODIFIED = "data modified"; //NOI18N
58
public static final String JavaDoc PROPERTY_DATA_UPDATED = "data changed"; //NOI18N
59
protected XmlMultiViewEditorSupport editorSupport;
60     private org.xml.sax.SAXException JavaDoc saxError;
61     
62     private final DataCache dataCache = new DataCache();
63     private EncodingHelper encodingHelper = new EncodingHelper();
64     private transient long timeStamp = 0;
65     private transient WeakReference JavaDoc lockReference;
66     
67     
68     private MultiViewElement activeMVElement;
69     
70     private final SaveCookie saveCookie = new SaveCookie() {
71         /** Implements <code>SaveCookie</code> interface. */
72         public void save() throws java.io.IOException JavaDoc {
73             getEditorSupport().saveDocument();
74         }
75     };
76     
77     /** Creates a new instance of XmlMultiViewDataObject */
78     public XmlMultiViewDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException {
79         super(pf, loader);
80         getCookieSet().add(XmlMultiViewEditorSupport.class, this);
81     }
82     
83     protected EditorCookie createEditorCookie() {
84         return getEditorSupport();
85     }
86     
87     public org.openide.nodes.Node.Cookie createCookie(Class JavaDoc clazz) {
88         if (clazz.isAssignableFrom(XmlMultiViewEditorSupport.class)) {
89             return getEditorSupport();
90         } else {
91             return null;
92         }
93     }
94     
95     /** Gets editor support for this data object. */
96     protected synchronized XmlMultiViewEditorSupport getEditorSupport() {
97         if(editorSupport == null) {
98             editorSupport = new XmlMultiViewEditorSupport(this);
99             editorSupport.getMultiViewDescriptions();
100         }
101         return editorSupport;
102     }
103     
104     /** enables to switch quickly to XML perspective in multi view editor
105      */

106     public void goToXmlView() {
107         getEditorSupport().goToXmlPerspective();
108     }
109     
110     protected void setSaxError(org.xml.sax.SAXException JavaDoc saxError) {
111         org.xml.sax.SAXException JavaDoc oldError = this.saxError;
112         this.saxError=saxError;
113         if (oldError==null) {
114             if (saxError != null) {
115                 firePropertyChange(PROP_DOCUMENT_VALID, Boolean.TRUE, Boolean.FALSE);
116             }
117         } else {
118             if (saxError == null) {
119                 firePropertyChange(PROP_DOCUMENT_VALID, Boolean.FALSE, Boolean.TRUE);
120             }
121         }
122         
123         String JavaDoc oldErrorMessage = getErrorMessage(oldError);
124         String JavaDoc newErrorMessage = getErrorMessage(saxError);
125         if (oldErrorMessage==null) {
126             if (newErrorMessage!=null) {
127                 firePropertyChange(PROP_SAX_ERROR, null, newErrorMessage);
128             }
129         } else if (!oldErrorMessage.equals(newErrorMessage)) {
130             firePropertyChange(PROP_SAX_ERROR, oldErrorMessage, newErrorMessage);
131         }
132     }
133     
134     private static String JavaDoc getErrorMessage(Exception JavaDoc e) {
135         return e == null ? null : e.getMessage();
136     }
137     
138     public org.xml.sax.SAXException JavaDoc getSaxError() {
139         return saxError;
140     }
141     
142     /** Icon for XML View */
143     protected java.awt.Image JavaDoc getXmlViewIcon() {
144         return org.openide.util.Utilities.loadImage("org/netbeans/modules/xml/multiview/resources/xmlObject.gif"); //NOI18N
145
}
146     
147     /** MultiViewDesc for MultiView editor
148      */

149     protected abstract DesignMultiViewDesc[] getMultiViewDesc();
150     
151     public void setLastOpenView(int index) {
152         getEditorSupport().setLastOpenView(index);
153     }
154     
155     /** provides renaming of super top component */
156     protected FileObject handleRename(String JavaDoc name) throws IOException {
157         FileObject retValue = super.handleRename(name);
158         getEditorSupport().updateDisplayName();
159         return retValue;
160     }
161     
162     /**
163      * Set whether the object is considered modified.
164      * Also fires a change event.
165      * If the new value is <code>true</code>, the data object is added into a {@link #getRegistry registry} of opened data objects.
166      * If the new value is <code>false</code>,
167      * the data object is removed from the registry.
168      */

169     public void setModified(boolean modif) {
170         super.setModified(modif);
171         //getEditorSupport().updateDisplayName();
172
if (modif) {
173             // Add save cookie
174
if (getCookie(SaveCookie.class) == null) {
175                 getCookieSet().add(saveCookie);
176             }
177         } else {
178             // Remove save cookie
179
if(saveCookie.equals(getCookie(SaveCookie.class))) {
180                 getCookieSet().remove(saveCookie);
181             }
182             
183         }
184     }
185     
186     public boolean canClose() {
187         final CloneableTopComponent topComponent = ((CloneableTopComponent) getEditorSupport().getMVTC());
188         if (topComponent != null){
189             Enumeration JavaDoc enumeration = topComponent.getReference().getComponents();
190             if (enumeration.hasMoreElements()) {
191                 enumeration.nextElement();
192                 if (enumeration.hasMoreElements()) {
193                     return true;
194                 }
195             }
196         }
197         FileLock lock;
198         try {
199             lock = waitForLock();
200         } catch (IOException e) {
201             ErrorManager.getDefault().notify(e);
202             return !isModified();
203         }
204         try {
205             return !isModified();
206         } finally {
207             lock.releaseLock();
208         }
209     }
210     
211     public FileLock waitForLock() throws IOException {
212         return waitForLock(10000);
213     }
214     
215     public FileLock waitForLock(long timeout) throws IOException {
216         long t = System.currentTimeMillis() + timeout;
217         long sleepTime = 50;
218         for (;;) {
219             try {
220                 return dataCache.lock();
221             } catch (IOException e) {
222                 if (System.currentTimeMillis() > t) {
223                     throw (IOException) new IOException("Cannot wait for data lock for more than " + timeout + " ms").initCause(e); //NO18N
224
}
225                 try {
226                     Thread.sleep(sleepTime);
227                     sleepTime = 3 * sleepTime / 2;
228                 } catch (InterruptedException JavaDoc e1) {
229                     //
230
}
231             }
232         }
233     }
234     
235     public org.netbeans.core.api.multiview.MultiViewPerspective getSelectedPerspective() {
236         return getEditorSupport().getSelectedPerspective();
237     }
238     
239     /** Enable to focus specific object in Multiview Editor
240      * The default implementation opens the XML View.
241      */

242     public void showElement(Object JavaDoc element) {
243         getEditorSupport().edit();
244     }
245     
246     /** Enable to get active MultiViewElement object
247      */

248     protected MultiViewElement getActiveMultiViewElement() {
249         return activeMVElement;
250     }
251     void setActiveMultiViewElement(MultiViewElement element) {
252         activeMVElement = element;
253     }
254     /** Opens the specific view
255      * @param index multi-view index
256      */

257     public void openView(int index) {
258         getEditorSupport().openView(index);
259     }
260     
261     protected abstract String JavaDoc getPrefixMark();
262     
263     boolean acceptEncoding() throws IOException {
264         encodingHelper.resetEncoding();
265         DataCache dataCache = getDataCache();
266         String JavaDoc s = dataCache.getStringData();
267         String JavaDoc encoding = encodingHelper.detectEncoding(s.getBytes());
268         if (!encodingHelper.getEncoding().equals(encoding)) {
269             Object JavaDoc result = showChangeEncodingDialog(encoding);
270             if (NotifyDescriptor.YES_OPTION.equals(result)) {
271                 dataCache.setData(encodingHelper.setDefaultEncoding(s));
272             } else if (NotifyDescriptor.NO_OPTION.equals(result)) {
273                 showUsingDifferentEncodingMessage(encoding);
274             } else {
275                 return false;
276             }
277         }
278         return true;
279     }
280     
281     private void showUsingDifferentEncodingMessage(String JavaDoc encoding) {
282         String JavaDoc message = NbBundle.getMessage(XmlMultiViewDataObject.class, "TEXT_TREAT_USING_DIFFERENT_ENCODING", encoding,
283                 encodingHelper.getEncoding());
284         NotifyDescriptor.Message descriptor = new NotifyDescriptor.Message(message);
285         descriptor.setTitle(getPrimaryFile().getPath());
286         DialogDisplayer.getDefault().notify(descriptor);
287     }
288     
289     private Object JavaDoc showChangeEncodingDialog(String JavaDoc encoding) {
290         String JavaDoc message = NbBundle.getMessage(Utils.class, "TEXT_CHANGE_DECLARED_ENCODING", encoding,
291                 encodingHelper.getEncoding());
292         NotifyDescriptor descriptor = new NotifyDescriptor.Confirmation(message, getPrimaryFile().getPath(),
293                 NotifyDescriptor.YES_NO_CANCEL_OPTION);
294         return DialogDisplayer.getDefault().notify(descriptor);
295     }
296     
297     public EncodingHelper getEncodingHelper() {
298         return encodingHelper;
299     }
300     
301     public DataCache getDataCache() {
302         return dataCache;
303     }
304     
305     /** Is that necesary for this class to be public ?
306      * It can be changed to interface
307      */

308     public class DataCache {
309         
310         // What about using the StringBuffer instead ?
311
private transient String JavaDoc buffer = null;
312         private long fileTime = 0;
313         
314         public void loadData() {
315             FileObject file = getPrimaryFile();
316             if (fileTime == file.lastModified().getTime()) {
317                 return;
318             }
319             try {
320                 FileLock dataLock = lock();
321                 loadData(file, dataLock);
322             } catch (IOException e) {
323                 if (buffer == null) {
324                     buffer = ""; //NOI18N
325
}
326             }
327         }
328         
329         /**
330          * Updates the data cache with the contents of the associated file.
331          * Unlike {@link #loadData()}, tries to use existing lock before attempting
332          * to acquire a new lock.
333          */

334         public void reloadData() throws IOException{
335             FileObject file = getPrimaryFile();
336             if (fileTime == file.lastModified().getTime()) {
337                 return;
338             }
339             FileLock lock = getLock();
340             if (lock == null){
341                 lock = lock();
342             }
343             loadData(file, lock);
344             
345         }
346         /** Does this method need to be public ?
347          */

348         public void loadData(FileObject file, FileLock dataLock) throws IOException {
349             try {
350                 BufferedInputStream inputStream = new BufferedInputStream(file.getInputStream());
351                 String JavaDoc encoding = encodingHelper.detectEncoding(inputStream);
352                 if (!encodingHelper.getEncoding().equals(encoding)) {
353                     showUsingDifferentEncodingMessage(encoding);
354                 }
355                 Reader reader = new InputStreamReader(inputStream, encodingHelper.getEncoding());
356                 long time;
357                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc(2048);
358                 try {
359                     char[] buf = new char[1024];
360                     time = file.lastModified().getTime();
361                     int i;
362                     while ((i = reader.read(buf,0,1024)) != -1) {
363                         sb.append(buf,0,i);
364                     }
365                 } finally {
366                     reader.close();
367                 }
368                 buffer = null;
369                 fileTime = time;
370                 setData(dataLock, sb.toString(), true);
371             } finally {
372                 dataLock.releaseLock();
373             }
374         }
375         /** Is the second argument necessary ?
376          */

377         public void setData(FileLock lock, String JavaDoc s, boolean modify) throws IOException {
378             testLock(lock);
379             boolean modified = isModified() || modify;
380             long oldTimeStamp = timeStamp;
381             if (setData(s)) {
382                 if (!modified) {
383                     saveData(lock);
384                     firePropertyChange(PROPERTY_DATA_UPDATED, new Long JavaDoc(oldTimeStamp), new Long JavaDoc(timeStamp));
385                 } else {
386                     firePropertyChange(PROPERTY_DATA_MODIFIED, new Long JavaDoc(oldTimeStamp), new Long JavaDoc(timeStamp));
387                 }
388             }
389         }
390         
391         private boolean setData(String JavaDoc s) {
392             // ??? when this can happen
393
if (s.equals(buffer)) {
394                 return false;
395             }
396             buffer = s;
397             long newTimeStamp = new Date JavaDoc().getTime();
398             if (newTimeStamp <= timeStamp) {
399                 newTimeStamp = timeStamp + 1;
400             }
401             timeStamp = newTimeStamp;
402             fileTime = 0;
403             return true;
404         }
405         
406         public synchronized void saveData(FileLock dataLock) {
407             if (buffer == null || fileTime == getPrimaryFile().lastModified().getTime()) {
408                 return;
409             }
410             
411             try {
412                 XmlMultiViewEditorSupport editorSupport = getEditorSupport();
413                 if (editorSupport.getDocument() == null) {
414                     XmlMultiViewEditorSupport.XmlEnv xmlEnv = editorSupport.getXmlEnv();
415                     FileLock lock = xmlEnv.takeLock();
416                     OutputStream outputStream = getPrimaryFile().getOutputStream(lock);
417                     Writer writer = new OutputStreamWriter(outputStream, encodingHelper.getEncoding());
418                     try {
419                         writer.write(buffer);
420                     } finally {
421                         writer.close();
422                         lock.releaseLock();
423                         xmlEnv.unmarkModified();
424                         resetFileTime();
425                     }
426                 } else {
427                     editorSupport.saveDocument(dataLock);
428                 }
429             } catch (IOException e) {
430                 ErrorManager.getDefault().notify(e);
431             }
432         }
433         
434         public FileLock lock() throws IOException {
435             FileLock current = getLock();
436             if (current != null) {
437                 throw new FileAlreadyLockedException("File is already locked by [" + current + "]."); // NO18N
438
}
439             FileLock l = new FileLock();
440             lockReference = new WeakReference JavaDoc(l);
441             return l;
442         }
443         
444         private FileLock getLock() {
445             // How this week reference can be useful ?
446
FileLock l = lockReference == null ? null : (FileLock) lockReference.get();
447             if (l != null && !l.isValid()) {
448                 l = null;
449             }
450             return l;
451         }
452         
453         public String JavaDoc getStringData() {
454             if (buffer == null) {
455                 loadData();
456             }
457             return buffer;
458         }
459         
460         public byte[] getData() {
461             try {
462                 return getStringData().getBytes(encodingHelper.getEncoding());
463             } catch (UnsupportedEncodingException e) {
464                 ErrorManager.getDefault().notify(e);
465                 return null; // should not happen
466
}
467         }
468         
469         public void setData(FileLock lock, byte[] data, boolean modify) throws IOException {
470             encodingHelper.detectEncoding(data);
471             setData(lock, new String JavaDoc(data, encodingHelper.getEncoding()), modify);
472         }
473         
474         public long getTimeStamp() {
475             return timeStamp;
476         }
477         
478         public InputStream createInputStream() {
479             try {
480                 encodingHelper.detectEncoding(getStringData().getBytes());
481                 return new ReaderInputStream(new StringReader(getStringData()), encodingHelper.getEncoding());
482             } catch (IOException e) {
483                 ErrorManager.getDefault().notify(e);
484                 return null;
485             }
486         }
487         
488         public Reader createReader() throws IOException {
489             return new StringReader(getStringData());
490         }
491         
492         public OutputStream createOutputStream() throws IOException {
493             final FileLock dataLock = lock();
494             return new ByteArrayOutputStream() {
495                 public void close() throws IOException {
496                     try {
497                         super.close();
498                         setData(dataLock, toByteArray(), true);
499                     } finally {
500                         dataLock.releaseLock();
501                     }
502                 }
503             };
504         }
505         
506         public OutputStream createOutputStream(final FileLock dataLock, final boolean modify) throws IOException {
507             testLock(dataLock);
508             return new ByteArrayOutputStream() {
509                 public void close() throws IOException {
510                     super.close();
511                     setData(dataLock, toByteArray(), modify);
512                     if (!modify) {
513                         dataCache.saveData(dataLock);
514                     }
515                 }
516             };
517         }
518         
519         public Writer createWriter() throws IOException {
520             final FileLock dataLock = lock();
521             return new StringWriter() {
522                 public void close() throws IOException {
523                     try {
524                         super.close();
525                         setData(dataLock, toString(), true);
526                     } finally {
527                         dataLock.releaseLock();
528                     }
529                 }
530             };
531         }
532         
533         public Writer createWriter(final FileLock dataLock, final boolean modify) throws IOException {
534             testLock(dataLock);
535             return new StringWriter() {
536                 public void close() throws IOException {
537                     super.close();
538                     setData(dataLock, toString(), modify);
539                     if (!modify) {
540                         dataCache.saveData(dataLock);
541                     }
542                 }
543             };
544         }
545         
546         public void testLock(FileLock lock) throws IOException {
547             if (lock == null) {
548                 throw new IOException("Lock is null."); //NO18N
549
} else if (lock != getLock()){
550                 throw new IOException("Invalid lock [" + lock + "]. Expected [" + getLock() + "]."); //NO18N
551
}
552         }
553         
554         public void resetFileTime() {
555             fileTime = getPrimaryFile().lastModified().getTime();
556         }
557     }
558 }
559
Popular Tags