KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > apisupport > project > CreatedModifiedFiles


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.apisupport.project;
21
22 import java.io.IOException JavaDoc;
23 import java.net.URL JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
31 import java.util.SortedSet JavaDoc;
32 import java.util.TreeSet JavaDoc;
33 import org.netbeans.api.project.Project;
34 import org.netbeans.modules.apisupport.project.layers.LayerUtils;
35 import org.openide.filesystems.FileObject;
36 import org.openide.filesystems.FileSystem;
37 import org.openide.filesystems.FileUtil;
38 import org.openide.modules.SpecificationVersion;
39
40 /**
41  * Provide general infrastructure for performing miscellaneous operations upon
42  * {@link NbModuleProject}'s files, such as <em>manifest.mf</em>,
43  * <em>bundle.properties</em>, <em>layer.xml</em>, <em>project.xml</em> easily.
44  * See javadoc to individual methods below. After creating a
45  * <code>CreatedModifiedFiles</code> instance client may create {@link
46  * CreatedModifiedFiles.Operation} which then may be added to the
47  * <code>CreatedModifiedFiles</code> instance or just used itself. Both
48  * <code>CreatedModifiedFiles</code> and <code>Operation</code> provide methods
49  * to get sets of relative (to a project's base directory) paths which are
50  * going to be created and/or modified. These sets may be obtained
51  * <strong>before</strong> added operation are run so they can be e.g. shown by
52  * wizard before any files are actually created.
53  *
54  * @author Martin Krauskopf
55  */

56 public final class CreatedModifiedFiles {
57     
58     /**
59      * Operation that may be added to a <code>CreatedModifiedFiles</code>
60      * instance or can just be used alone. See {@link CreatedModifiedFiles} for
61      * more information.
62      */

63     public interface Operation {
64         
65         /** Perform this operation. */
66         void run() throws IOException JavaDoc;
67         
68         /**
69          * Returns sorted array of path which are going to modified after this
70          * {@link CreatedModifiedFiles} instance is run. Paths are relative to
71          * the project's base directory. It is available immediately after an
72          * operation instance is created.
73          * XXX why is this sorted, and not a simple Set<String>?
74          */

75         String JavaDoc[] getModifiedPaths();
76         
77         /**
78          * Returns sorted array of path which are going to created after this
79          * {@link CreatedModifiedFiles} instance is run. Paths are relative to
80          * the project's base directory. It is available immediately after an
81          * operation instance is created.
82          */

83         String JavaDoc[] getCreatedPaths();
84         
85         /**
86          * returns paths that are already existing but the operaton expects to create it.
87          * Is an error condition and should be shown in UI.
88          *
89          */

90         String JavaDoc[] getInvalidPaths();
91         
92         /* XXX should perhaps also have:
93         /**
94          * True if the created or modified path is relevant to the user and should
95          * be selected in the final wizard.
96          * /
97         boolean isRelevant(String path);
98         /**
99          * True if the created or modified path should be opened in the editor.
100          * /
101         boolean isForEditing(String path);
102          */

103         
104     }
105     
106     private final SortedSet JavaDoc<String JavaDoc> createdPaths = new TreeSet JavaDoc();
107     private final SortedSet JavaDoc<String JavaDoc> modifiedPaths = new TreeSet JavaDoc();
108     private final SortedSet JavaDoc<String JavaDoc> invalidPaths = new TreeSet JavaDoc();
109     
110     /** {@link Project} this instance manage. */
111     private final Project project;
112     private final List JavaDoc<CreatedModifiedFiles.Operation> operations = new ArrayList JavaDoc();
113     
114     // For use from CreatedModifiedFilesFactory.LayerModifications; XXX would be better to have an operation context or similar
115
// (so that multiple operations could group pre- and post-actions)
116
private LayerUtils.LayerHandle layerHandle;
117     LayerUtils.LayerHandle getLayerHandle() {
118         if (layerHandle == null) {
119             layerHandle = LayerUtils.layerForProject(project);
120         }
121         return layerHandle;
122     }
123     
124     /**
125      * Create instance for managing given {@link NbModuleProject}'s files.
126      * @param project project this instance will operate upon
127      */

128     public CreatedModifiedFiles(Project project) {
129         this.project = project;
130     }
131     
132     /**
133      * Adds given {@link Operation} to a list of operations that will be run
134      * after calling {@link #run()}. Operations are run in the order in which
135      * they have been added. Also files which would be created by a given
136      * operation are added to lists of paths returned by {@link
137      * #getModifiedPaths()} or {@link #getCreatedPaths()} immediately. @param
138      * operation operation to be added
139      */

140     public void add(Operation operation) {
141         operations.add(operation);
142         // XXX should always show isForEditing files at the top of the list, acc. to Jano
143
createdPaths.addAll(Arrays.asList(operation.getCreatedPaths()));
144         modifiedPaths.addAll(Arrays.asList(operation.getModifiedPaths()));
145         invalidPaths.addAll(Arrays.asList(operation.getInvalidPaths()));
146     }
147     
148     /**
149      * Performs in turn {@link Operation#run()} on all operations added to this
150      * instance in order in which operations have been added.
151      */

152     public void run() throws IOException JavaDoc {
153         boolean oldAutosave = false;
154         if (layerHandle != null) {
155             oldAutosave = layerHandle.isAutosave();
156             layerHandle.setAutosave(false);
157         }
158         try {
159             for (Iterator JavaDoc it = operations.iterator(); it.hasNext(); ) {
160                 Operation op = (Operation) it.next();
161                 op.run();
162             }
163             if (layerHandle != null) {
164                 // XXX clumsy, see above
165
layerHandle.save();
166             }
167         } finally {
168             if (layerHandle != null) {
169                 layerHandle.setAutosave(oldAutosave);
170             }
171         }
172         // XXX should get EditCookie/OpenCookie for created/modified files for which isForEditing
173
// XXX should return a Set<FileObject> of created/modified files for which isRelevant
174
}
175     
176     public String JavaDoc[] getCreatedPaths() {
177         if (createdPaths == null) {
178             return new String JavaDoc[0];
179         } else {
180             String JavaDoc[] s = new String JavaDoc[createdPaths.size()];
181             return (String JavaDoc[]) createdPaths.toArray(s);
182         }
183     }
184     
185     public String JavaDoc[] getModifiedPaths() {
186         if (modifiedPaths == null) {
187             return new String JavaDoc[0];
188         } else {
189             String JavaDoc[] s = new String JavaDoc[modifiedPaths.size()];
190             return (String JavaDoc[]) modifiedPaths.toArray(s);
191         }
192     }
193     
194     public String JavaDoc[] getInvalidPaths() {
195         if (invalidPaths == null) {
196             return new String JavaDoc[0];
197         } else {
198             String JavaDoc[] s = new String JavaDoc[invalidPaths.size()];
199             return (String JavaDoc[]) invalidPaths.toArray(s);
200         }
201     }
202     
203     /**
204      * Returns {@link Operation} for creating custom file in the project file
205      * hierarchy.
206      * @param path relative to a project directory where a file to be created
207      * @param content content for the file being created. Content may address
208      * either text or binary data.
209      */

210     public Operation createFile(String JavaDoc path, URL JavaDoc content) {
211         return CreatedModifiedFilesFactory.createFile(project, path, content);
212     }
213     
214     /**
215      * Returns an {@link Operation} for creating custom file in the project
216      * file hierarchy with an option to replace <em>token</em>s from a given
217      * <code>content</code> with custom string. The result will be stored into
218      * a file representing by a given <code>path</code>.
219      *
220      * @param path relative to a project directory where a file to be created
221      * @param content content for the file being created
222      * @param tokens map of <em>token to be replaced</em> - <em>by what</em>
223      * pairs which will be applied on the stored file. Both a key and a
224      * value have to be a valid regular expression. See {@link
225      * java.lang.String#replaceAll(String, String)} and follow links in
226      * its javadoc for more details. May be <code>null</code> (the same
227      * as an empty map).
228      */

229     public Operation createFileWithSubstitutions(String JavaDoc path,
230             URL JavaDoc content, Map JavaDoc<String JavaDoc,String JavaDoc> tokens) {
231         return CreatedModifiedFilesFactory.createFileWithSubstitutions(project, path, content, tokens);
232     }
233     
234     /**
235      * Provides {@link Operation} that will add given <code>value</code> under
236      * a specified <code>key</code> into the custom <em>bundle</em> which is
237      * specified by the <code>bundlePath</code> parameter.
238      */

239     public Operation bundleKey(String JavaDoc bundlePath, String JavaDoc key, String JavaDoc value) {
240         return CreatedModifiedFilesFactory.bundleKey(project, key, value, bundlePath);
241     }
242     
243     /**
244      * Provides {@link Operation} that will add given <code>value</code> under
245      * a specified <code>key</code> into the project's default <em>localized
246      * bundle</em> which is specified in the project's <em>manifest</em>.
247      */

248     public Operation bundleKeyDefaultBundle(String JavaDoc key, String JavaDoc value) {
249         return CreatedModifiedFilesFactory.bundleKeyDefaultBundle(project, key, value);
250     }
251     
252     /**
253      * Provides {@link Operation} that will create a new section in the
254      * project's <em>manifest</em> registering a given
255      * <code>dataLoaderClass</code>.
256      *
257      * <pre>
258      * Name: org/netbeans/modules/myprops/MyPropsLoader.class
259      * OpenIDE-Module-Class: Loader
260      * </pre>
261      *
262      * @param dataLoaderClass e.g. org/netbeans/modules/myprops/MyPropsLoader
263      * (<strong>without</strong> .class extension)
264      * @param installBefore content of Install-Before attribute, or null if not
265      * specified
266      */

267     public Operation addLoaderSection(String JavaDoc dataLoaderClass, String JavaDoc installBefore) {
268         return CreatedModifiedFilesFactory.addLoaderSection(project, dataLoaderClass, installBefore);
269     }
270     
271     /**
272      * Provides {@link Operation} that will register an <code>implClass</code>
273      * implementation of <code>interfaceClass</code> interface in the lookup.
274      * If a file representing <code>interfaceClass</code> service already
275      * exists in <em>META-INF/services</em> directory
276      * <code>implClass</code> will be appended to the end of the list of
277      * implementations. If it doesn't exist a new file will be created.
278      *
279      * @param interfaceClass e.g. org.example.spi.somemodule.ProvideMe
280      * @param implClass e.g. org.example.module1.ProvideMeImpl
281      * @param inTests if true, add to test/unit/src/META-INF/services/, else to src/META-INF/services/
282      */

283     public Operation addLookupRegistration(String JavaDoc interfaceClass, String JavaDoc implClass, boolean inTests) {
284         return CreatedModifiedFilesFactory.addLookupRegistration(
285                 project, interfaceClass, implClass, inTests);
286     }
287     
288     /**
289      * Add a dependency to a list of module dependencies of this project. This
290      * means editing of project's <em>nbproject/project.xml</em>. All
291      * parameters refers to a module this module will depend on. If a project
292      * already has a given dependency it will not be added.
293      *
294      * @param codeNameBase codename base
295      * @param releaseVersion release version, if <code>null</code> will be taken from the
296      * entry found in platform
297      * @param version specification version (see {@link SpecificationVersion}),
298      * if null will be taken from the entry found in platform
299      * @param useInCompiler do this module needs a module beeing added at a
300      * compile time?
301      */

302     public Operation addModuleDependency(String JavaDoc codeNameBase, String JavaDoc
303             releaseVersion, SpecificationVersion version, boolean useInCompiler) {
304         return CreatedModifiedFilesFactory.addModuleDependency(project, codeNameBase,
305                 releaseVersion, version, useInCompiler);
306     }
307     
308     /**
309      * Delegates to {@link #addModuleDependency(String, String,
310      * SpecificationVersion, boolean)} passing a given code name base,
311      * <code>null</code> as release version, <code>null</code> as version and
312      * <code>true</code> as useInCompiler arguments.
313      */

314     public CreatedModifiedFiles.Operation addModuleDependency(String JavaDoc codeNameBase) {
315         return addModuleDependency(codeNameBase, null, null, true);
316     }
317     
318     /**
319      * Creates an entry (<em>file</em> element) in the project's layer. Also
320      * may create and/or modify other files as it is needed.
321      *
322      * @param layerPath path in a project's layer. Folders which don't exist
323      * yet will be created. (e.g.
324      * <em>Menu/Tools/org-example-module1-BeepAction.instance</em>).
325      * @param content became content of a file, or null
326      * @param substitutionTokens map of <em>token to be replaced</em> - <em>by
327      * what</em> pairs which will be applied on the stored
328      * <code>content</code> file. Both a key and a value have to be a
329      * valid regular expression. See {@link
330      * java.lang.String#replaceAll(String, String)} and follow links in
331      * its javadoc for more details. May be <code>null</code> (the same
332      * as an empty map).
333      * @param localizedDisplayName if it is not a <code>null</code>
334      * <em>SystemFileSystem.localizingBundle</em> attribute will be
335      * created with the stringvalue to a default bundle (from manifest).
336      * Also an appropriate entry will be added into the bundle.
337      * @param fileAttributes &lt;String,Object&gt; map. key in the map is the
338      * name of the file attribute value is the actual value, currently
339      * supported types are Boolean and String Generates
340      * <pre>
341      * &lt;attr name="KEY" stringvalue="VALUE"/&gt; or &lt;attr name="KEY" booleanvalue="VALUE"/&gt;
342      * </pre>
343      * @return see {@link Operation}
344      */

345     public Operation createLayerEntry(
346             String JavaDoc layerPath,
347             URL JavaDoc content,
348             Map JavaDoc<String JavaDoc,String JavaDoc> substitutionTokens,
349             String JavaDoc localizedDisplayName,
350             Map JavaDoc<String JavaDoc,Object JavaDoc> fileAttributes) {
351         return CreatedModifiedFilesFactory.createLayerEntry(this, project, layerPath,
352                 content, substitutionTokens,
353                 localizedDisplayName, fileAttributes);
354     }
355     
356     /**
357      * Adds new attributes into manifest file.
358      * @param section the name of the section or <code>null</code> for the main section.
359      * @param attributes &lt;String,String&gt; map mapping attributes names and values.
360      * @return see {@link Operation}
361      */

362     public Operation manifestModification(String JavaDoc section, Map JavaDoc<String JavaDoc,String JavaDoc> attributes) {
363         return CreatedModifiedFilesFactory.manifestModification(project, section, attributes);
364     }
365     
366     /**
367      * Adds new properties into property file.
368      * @param propertyPath path representing properties file relative to a project directory where all
369      * properties will be put in. If such a file does not exist it is created.
370      * @param properties &lt;String,String&gt; map mapping properties names and values.
371      * @return see {@link Operation}
372      */

373     public Operation propertiesModification(String JavaDoc propertyPath,
374             Map JavaDoc<String JavaDoc,String JavaDoc> properties) {
375         return CreatedModifiedFilesFactory.propertiesModification(project, propertyPath, properties);
376     }
377     
378     /**
379      * Creates a new arbitrary <em>&lt;attr&gt;</em> element.
380      *
381      * @param parentPath path to a <em>file</em> or a <em>folder</em> in a
382      * project's layer. It <strong>must</strong> exist.
383      * @param attrName value of the name attribute of the <em>&lt;attr&gt;</em>
384      * element.
385      * @param attrValue value of the attribute (may specially be a string prefixed with "newvalue:" or "methodvalue:")
386      * @return see {@link Operation}
387      */

388     public CreatedModifiedFiles.Operation createLayerAttribute(final String JavaDoc parentPath,
389             final String JavaDoc attrName, final Object JavaDoc attrValue) {
390         return layerModifications(new LayerOperation() {
391             public void run(FileSystem layer) throws IOException JavaDoc {
392                 FileObject f = layer.findResource(parentPath);
393                 if (f == null) {
394                     // XXX sometimes this happens when it should not, during unit tests... why?
395
/*
396                     try {
397                         // For debugging:
398                         getLayerHandle().save();
399                     } catch (IOException e) {
400                         e.printStackTrace();
401                     }
402                      */

403                     throw new IOException JavaDoc(parentPath);
404                 }
405                 f.setAttribute(attrName, attrValue);
406             }
407         }, Collections.EMPTY_SET);
408     }
409     
410     /**
411      * Order two entries in a project layer. i.e. creates an ordering
412      * <em>&lt;attr&gt;</em> element.
413      *
414      * @param layerPath folder path in a project's layer. Folders which don't
415      * exist yet will be created. (e.g. <em>Loaders/text/x-java/Actions</em>).
416      * @param precedingItemName item to be before <em>followingItemName</em>
417      * @param followingItemName item to be after <em>precedingItemName</em>
418      */

419     public Operation orderLayerEntry(final String JavaDoc layerPath, final String JavaDoc precedingItemName,
420             final String JavaDoc followingItemName) {
421         return layerModifications(new LayerOperation() {
422             public void run(FileSystem layer) throws IOException JavaDoc {
423                 FileObject f = FileUtil.createFolder(layer.getRoot(), layerPath);
424                 f.setAttribute(precedingItemName + '/' + followingItemName, Boolean.TRUE);
425             }
426         }, Collections.EMPTY_SET);
427     }
428     
429     /**
430      * Make structural modifications to the project's XML layer.
431      * The operations may be expressed as filesystem calls.
432      * @param op a callback for the actual changes to make
433      * @param externalFiles a list of <em>simple filenames</em> of new data files which
434      * are to be created in the layer and which will therefore appear
435      * on disk alongside the layer, usually with the same names (unless
436      * they conflict with existing files); you still need to create them
437      * yourself using e.g. {@link FileObject#createData} and {@link FileObject#getOutputStream}
438      * @return the operation handle
439      */

440     public Operation layerModifications(final LayerOperation op, final Set JavaDoc<String JavaDoc> externalFiles) {
441         return CreatedModifiedFilesFactory.layerModifications(project, op, externalFiles, this);
442     }
443     
444     /**
445      * Callback for modifying the project's XML layer.
446      * @see #layerModifications
447      */

448     public interface LayerOperation {
449         
450         /**
451          * Actually change the layer.
452          * @param layer the layer to make changes to using Filesystems API calls
453          * @throws IOException if the changes fail somehow
454          */

455         void run(FileSystem layer) throws IOException JavaDoc;
456         
457     }
458     
459 }
460
Popular Tags