KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > properties > PropertiesFileEntry


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.properties;
21
22 import java.io.ByteArrayOutputStream JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.ObjectInputStream JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.TreeSet JavaDoc;
31 import org.openide.filesystems.FileLock;
32 import org.openide.filesystems.FileObject;
33 import org.openide.loaders.MultiDataObject;
34 import org.openide.nodes.Children;
35 import org.openide.nodes.CookieSet;
36 import org.openide.nodes.Node;
37 import org.openide.NotifyDescriptor;
38 import org.openide.DialogDisplayer;
39 import org.openide.util.HelpCtx;
40 import org.openide.util.NbBundle;
41
42 /**
43  * Item in a set of properties files represented by a single
44  * <code>PropertiesDataObject</code>.
45  *
46  * @see PropertiesDataLoader#createPrimaryEntry
47  * @see PropertiesDataLoader#createSecondaryEntry
48  */

49 public class PropertiesFileEntry extends PresentableFileEntry
50                                  implements CookieSet.Factory {
51
52     /** Basic name of bundle .properties file. */
53     private String JavaDoc basicName;
54     
55     /** Structure handler for .properties file represented by this instance. */
56     private transient StructHandler propStruct;
57     
58     /** Editor support for this entry. */
59     private transient PropertiesEditorSupport editorSupport;
60
61     /** Generated serial version UID. */
62     static final long serialVersionUID = -3882240297814143015L;
63     
64     
65     /**
66      * Creates a new <code>PropertiesFileEntry</code>.
67      *
68      * @param obj data object this entry belongs to
69      * @param file file object for this entry
70      */

71     PropertiesFileEntry(MultiDataObject obj, FileObject file) {
72         super(obj, file);
73         FileObject fo = getDataObject().getPrimaryFile();
74         if (fo == null)
75             // primary file not init'ed yet => I'm the primary entry
76
basicName = getFile().getName();
77         else
78             basicName = fo.getName();
79         
80         getCookieSet().add(PropertiesEditorSupport.class, this);
81     }
82
83     
84     /** Copies entry to folder. Overrides superclass method.
85      * @param folder folder where copy
86      * @param suffix suffix to use
87      * @exception IOException when error happens */

88     public FileObject copy(FileObject folder, String JavaDoc suffix) throws IOException JavaDoc {
89         String JavaDoc pasteSuffix = ((PropertiesDataObject)getDataObject()).getPasteSuffix();
90         
91         if(pasteSuffix == null)
92             return super.copy(folder, suffix);
93         
94         FileObject fileObject = getFile();
95         
96         String JavaDoc basicName = getDataObject().getPrimaryFile().getName();
97         String JavaDoc newName = basicName + pasteSuffix + Util.getLocaleSuffix(this);
98         
99         return fileObject.copy(folder, newName, fileObject.getExt());
100     }
101     
102     /** Deletes file. Overrides superclass method. */
103     public void delete() throws IOException JavaDoc {
104         getHandler().stopParsing();
105
106         try {
107             super.delete();
108         } finally {
109             // Sets back parsing flag.
110
getHandler().allowParsing();
111         }
112     }
113    
114     /** Moves entry to folder. Overrides superclass method.
115      * @param folder folder where copy
116      * @param suffix suffix to use
117      * @exception IOException when error happens */

118     public FileObject move(FileObject folder, String JavaDoc suffix) throws IOException JavaDoc {
119         String JavaDoc pasteSuffix = ((PropertiesDataObject)getDataObject()).getPasteSuffix();
120
121         if(pasteSuffix == null)
122             return super.move(folder, suffix);
123
124         FileObject fileObject = getFile();
125         FileLock lock = takeLock ();
126
127         try {
128             String JavaDoc basicName = getDataObject().getPrimaryFile().getName();
129             String JavaDoc newName = basicName + pasteSuffix + Util.getLocaleSuffix(this);
130
131             return fileObject.move (lock, folder, newName, fileObject.getExt());
132         } finally {
133             lock.releaseLock ();
134         }
135     }
136     
137     /** Implements <code>CookieSet.Factory</code> interface method. */
138     @SuppressWarnings JavaDoc("unchecked")
139     public <T extends Node.Cookie> T createCookie(Class JavaDoc<T> clazz) {
140         if (clazz.isAssignableFrom(PropertiesEditorSupport.class)) {
141             return (T) getPropertiesEditor();
142         } else {
143             return null;
144         }
145     }
146     
147     /** Creates a node delegate for this entry. Implements superclass abstract method. */
148     protected Node createNodeDelegate() {
149         return new PropertiesLocaleNode(this);
150     }
151
152     /** Gets children for this file entry. */
153     public Children getChildren() {
154         return new PropKeysChildren();
155     }
156
157     /** Gets struct handler for this entry.
158      * @return <StructHanlder</code> for this entry */

159     public StructHandler getHandler() {
160         if (propStruct == null) {
161             propStruct = new StructHandler(this);
162         }
163         return propStruct;
164     }
165
166     /** Deserialization. */
167     private void readObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
168         in.defaultReadObject();
169     }
170
171     /** Gets editor support for this entry.
172      * @return <code>PropertiesEditorSupport</code> instance for this entry */

173     protected PropertiesEditorSupport getPropertiesEditor() {
174         // Hack to ensure open support is created.
175
// PENDING has to be made finer.
176
getDataObject().getCookie(PropertiesOpen.class);
177         
178         if(editorSupport == null) {
179             synchronized(this) {
180                 if(editorSupport == null)
181                     editorSupport = new PropertiesEditorSupport(this);
182             }
183         }
184             
185         return editorSupport;
186     }
187
188     /** Renames underlying fileobject. This implementation returns the same file.
189      * Overrides superclass method.
190      *
191      * @param name new base name of the bundle
192      * @return file object with renamed file
193      */

194     public FileObject rename (String JavaDoc name) throws IOException JavaDoc {
195     
196         if (!getFile().getName().startsWith(basicName))
197             throw new IllegalStateException JavaDoc("Resource Bundles: error in Properties loader/rename."); // NOI18N
198

199         FileObject fo = super.rename(name + getFile().getName().substring(basicName.length()));
200         basicName = name;
201         return fo;
202     }
203
204     /** Renames underlying fileobject. This implementation returns the same file.
205      * Overrides superclass method.
206      *
207      * @param name full name of the file represented by this entry
208      * @return file object with renamed file
209      */

210     public FileObject renameEntry (String JavaDoc name) throws IOException JavaDoc {
211
212         if (!getFile().getName().startsWith(basicName))
213             throw new IllegalStateException JavaDoc("Resource Bundles: error in Properties loader / rename"); // NOI18N
214

215         if (basicName.equals(getFile().getName())) {
216             // primary entry - can not rename
217
NotifyDescriptor.Message msg = new NotifyDescriptor.Message(
218                 NbBundle.getBundle(PropertiesDataLoader.class).getString("MSG_AttemptToRenamePrimaryFile"),
219                 NotifyDescriptor.ERROR_MESSAGE);
220             DialogDisplayer.getDefault().notify(msg);
221             return getFile();
222         }
223
224         FileObject fo = super.rename(name);
225
226         // to notify the bundle structure that name of one file was changed
227
((PropertiesDataObject)getDataObject()).getBundleStructure().notifyOneFileChanged(getHandler());
228         
229         return fo;
230     }
231
232     public FileObject createFromTemplate (FileObject folder, String JavaDoc name) throws IOException JavaDoc {
233         if (!getFile().getName().startsWith(basicName))
234             throw new IllegalStateException JavaDoc("Resource Bundles: error in Properties createFromTemplate"); // NOI18N
235

236         String JavaDoc suffix = getFile ().getName ().substring (basicName.length ());
237         String JavaDoc nuename = name + suffix;
238         String JavaDoc ext = getFile ().getExt ();
239         FileObject existing = folder.getFileObject (nuename, ext);
240         if (existing == null) {
241             return super.createFromTemplate (folder, nuename);
242         } else {
243             // Append new content. Used to ask you whether the leave the old
244
// file alone, or overwrite it with the new file, or append the new
245
// content; but it can just cause deadlocks (#38599) to try to prompt
246
// the user for anything from inside a Datasystems method, so don't
247
// bother. Appending is the safest option; redundant stuff can always
248
// be cleaned up manually.
249
{ // avoiding reindenting code
250
byte[] originalData;
251                 byte[] buf = new byte[4096];
252                 int count;
253                 FileLock lock = existing.lock ();
254                 try {
255                     InputStream JavaDoc is = existing.getInputStream ();
256                     try {
257                         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc ((int) existing.getSize ());
258                         try {
259                             while ((count = is.read (buf)) != -1) {
260                                 baos.write (buf, 0, count);
261                             }
262                         } finally {
263                             originalData = baos.toByteArray ();
264                             baos.close ();
265                         }
266                     } finally {
267                         is.close ();
268                     }
269                     existing.delete (lock);
270                 } finally {
271                     lock.releaseLock ();
272                 }
273                 FileObject nue = folder.createData (nuename, ext);
274                 lock = nue.lock ();
275                 try {
276                     OutputStream JavaDoc os = nue.getOutputStream (lock);
277                     try {
278                         os.write (originalData);
279                         InputStream JavaDoc is = getFile ().getInputStream ();
280                         try {
281                             while ((count = is.read (buf)) != -1) {
282                                 os.write (buf, 0, count);
283                             }
284                         } finally {
285                             is.close ();
286                         }
287                     } finally {
288                         os.close ();
289                     }
290                 } finally {
291                     lock.releaseLock ();
292                 }
293                 // Does not appear to have any effect:
294
// ((PropertiesDataObject) getDataObject ()).getBundleStructure ().
295
// notifyOneFileChanged (getHandler ());
296
return nue;
297             }
298         }
299     }
300
301     /** Whether the object may be deleted. Implemenst superclass abstract method.
302      * @return <code>true</code> if it may (primary file can't be deleted)
303      */

304     public boolean isDeleteAllowed() {
305         // PENDING - better implementation : don't allow deleting Bunlde_en when Bundle_en_US exists
306
return (getFile ().canWrite ()) && (!basicName.equals(getFile().getName()));
307     }
308
309     /** Whether the object may be copied. Implements superclass abstract method.
310      * @return <code>true</code> if it may
311      */

312     public boolean isCopyAllowed() {
313         return true;
314     }
315     // [PENDING] copy should be overridden because e.g. copy and then paste
316
// to the same folder creates a new locale named "1"! (I.e. "foo_1.properties")
317

318     /** Indicates whether the object can be moved. Implements superclass abstract method.
319      * @return <code>true</code> if the object can be moved */

320     public boolean isMoveAllowed() {
321         return (getFile().canWrite()) && (getDataObject().getPrimaryEntry() != this);
322     }
323
324     /** Getter for rename action. Implements superclass abstract method.
325      * @return true if the object can be renamed
326      */

327     public boolean isRenameAllowed () {
328         return getFile ().canWrite ();
329     }
330
331     /** Help context for this object. Implements superclass abstract method.
332      * @return help context
333      */

334     public HelpCtx getHelpCtx() {
335         return new HelpCtx(Util.HELP_ID_CREATING);
336     }
337
338     
339     /** Children of a node representing single properties file.
340      * Contains nodes representing individual properties (key-value pairs with comments). */

341     private class PropKeysChildren extends Children.Keys<String JavaDoc> {
342
343         /** Listens to changes on the property bundle structure. */
344         private PropertyBundleListener bundleListener = null;
345
346         
347         /** Constructor. */
348         PropKeysChildren() {
349             super();
350         }
351
352         
353         /** Sets all keys in the correct order. Calls <code>setKeys</code>. Helper method.
354          * @see org.openide.nodes.Children.Keys#setKeys(java.util.Collection) */

355         private void mySetKeys() {
356             // Use TreeSet because its iterator iterates in ascending order.
357
Set JavaDoc<String JavaDoc> keys = new TreeSet JavaDoc<String JavaDoc>(new KeyComparator());
358             PropertiesStructure propStructure = getHandler().getStructure();
359             if (propStructure != null) {
360                 for (Iterator JavaDoc<Element.ItemElem> iterator = propStructure.allItems(); iterator.hasNext(); ) {
361                     Element.ItemElem item = iterator.next();
362                     if (item != null && item.getKey() != null) {
363                         keys.add(item.getKey());
364                     }
365                 }
366             }
367             
368             setKeys(keys);
369         }
370
371         /** Called to notify that the children has been asked for children
372          * after and that they should set its keys. Overrides superclass method.
373          */

374         protected void addNotify () {
375             mySetKeys();
376
377             bundleListener = new PropertyBundleListener () {
378                 public void bundleChanged(PropertyBundleEvent evt) {
379                     int changeType = evt.getChangeType();
380                     
381                     if(changeType == PropertyBundleEvent.CHANGE_STRUCT
382                         || changeType == PropertyBundleEvent.CHANGE_ALL) {
383                         mySetKeys();
384                     } else if(changeType == PropertyBundleEvent.CHANGE_FILE
385                         && evt.getEntryName().equals(getFile().getName())) {
386                             
387                         // File underlying this entry changed.
388
mySetKeys();
389                     }
390                 }
391             }; // End of annonymous class.
392

393             bundleStructure().addPropertyBundleListener(bundleListener);
394         }
395
396         /** Called to notify that the children has lost all of its references to
397          * its nodes associated to keys and that the keys could be cleared without
398          * affecting any nodes (because nobody listens to that nodes). Overrides superclass method.
399          */

400         protected void removeNotify () {
401             bundleStructure().removePropertyBundleListener(bundleListener);
402             setKeys(new ArrayList JavaDoc<String JavaDoc>());
403         }
404
405         /** Create nodes. Implements superclass abstract method. */
406         protected Node[] createNodes (String JavaDoc itemKey) {
407             return new Node[] { new KeyNode(getHandler().getStructure(), itemKey) };
408         }
409
410         /** Model accessor method. */
411         private BundleStructure bundleStructure() {
412             return ((PropertiesDataObject)PropertiesFileEntry.this.getDataObject()).getBundleStructure();
413         }
414     } // End of inner class PropKeysChildren.
415

416 }
417
Popular Tags