KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > j2seproject > UpdateHelper


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.java.j2seproject;
21
22 import java.io.IOException JavaDoc;
23 import javax.swing.JButton JavaDoc;
24 import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties;
25 import org.netbeans.modules.websvc.api.jaxws.project.GeneratedFilesHelper;
26 import org.w3c.dom.Comment JavaDoc;
27 import org.w3c.dom.Document JavaDoc;
28 import org.w3c.dom.Element JavaDoc;
29 import org.w3c.dom.NamedNodeMap JavaDoc;
30 import org.w3c.dom.Node JavaDoc;
31 import org.w3c.dom.NodeList JavaDoc;
32 import org.w3c.dom.Text JavaDoc;
33 import org.openide.DialogDisplayer;
34 import org.openide.ErrorManager;
35 import org.openide.NotifyDescriptor;
36 import org.openide.util.NbBundle;
37 import org.openide.util.Mutex;
38 import org.netbeans.api.project.Project;
39 import org.netbeans.api.project.ProjectManager;
40 import org.netbeans.spi.project.AuxiliaryConfiguration;
41 import org.netbeans.spi.project.support.ant.AntProjectHelper;
42 import org.netbeans.spi.project.support.ant.EditableProperties;
43
44
45 /**
46  * Proxy for the AntProjectHelper which defers the update of the project metadata
47  * to explicit user action. Currently it is hard coded for update from
48  * "http://www.netbeans.org/ns/j2se-project/1" to "http://www.netbeans.org/ns/j2se-project/2".
49  * In future it should define plugable SPI.
50  */

51 public class UpdateHelper {
52
53     private static final boolean TRANSPARENT_UPDATE = Boolean.getBoolean("j2seproject.transparentUpdate");
54     private static final String JavaDoc BUILD_NUMBER = System.getProperty("netbeans.buildnumber"); // NOI18N
55
private static final String JavaDoc MINIMUM_ANT_VERSION_ELEMENT = "minimum-ant-version";
56
57     private final Project project;
58     private final AntProjectHelper helper;
59     private final AuxiliaryConfiguration cfg;
60     private final GeneratedFilesHelper genFileHelper;
61     private final Notifier notifier;
62     private boolean alreadyAskedInWriteAccess;
63     private Boolean JavaDoc isCurrent;
64     private Element JavaDoc cachedElement;
65
66     /**
67      * Creates new UpdateHelper
68      * @param project
69      * @param helper AntProjectHelper
70      * @param cfg AuxiliaryConfiguration
71      * @param genFileHelper GeneratedFilesHelper
72      * @param notifier used to ask user about project update
73      */

74     UpdateHelper (Project project, AntProjectHelper helper, AuxiliaryConfiguration cfg, GeneratedFilesHelper genFileHelper, Notifier notifier) {
75         assert project != null && helper != null && cfg != null && genFileHelper != null && notifier != null;
76         this.project = project;
77         this.helper = helper;
78         this.cfg = cfg;
79         this.genFileHelper = genFileHelper;
80         this.notifier = notifier;
81     }
82
83     /**
84      * Returns the AntProjectHelper.getProperties(), {@link AntProjectHelper#getProperties(String)}
85      * @param path a relative URI in the project directory.
86      * @return a set of properties
87      */

88     public EditableProperties getProperties (final String JavaDoc path) {
89         //Properties are the same in both j2seproject/1 and j2seproject/2
90
return ProjectManager.mutex().readAccess(new Mutex.Action<EditableProperties>(){
91             public EditableProperties run() {
92                 if (!isCurrent() && AntProjectHelper.PROJECT_PROPERTIES_PATH.equals(path)) { //Only project properties were changed
93
return getUpdatedProjectProperties ();
94                 }
95                 else {
96                     return helper.getProperties(path);
97                 }
98             }
99         });
100     }
101
102     /**
103      * In the case that the project is of current version or the properties are not {@link AntProjectHelper#PROJECT_PROPERTIES_PATH}
104      * it calls AntProjectHelper.putProperties(), {@link AntProjectHelper#putProperties(String, EditableProperties)}
105      * otherwise it asks user to updata project. If the user agrees with the project update, it does the update and calls
106      * AntProjectHelper.putProperties().
107      * @param path a relative URI in the project directory.
108      * @param props a set of properties
109      */

110     public void putProperties (final String JavaDoc path, final EditableProperties props) {
111         ProjectManager.mutex().writeAccess(
112             new Runnable JavaDoc () {
113                 public void run() {
114                     if (isCurrent() || !AntProjectHelper.PROJECT_PROPERTIES_PATH.equals(path)) { //Only project props should cause update
115
helper.putProperties(path,props);
116                     }
117                     else if (canUpdate()) {
118                         try {
119                             saveUpdate ();
120                             helper.putProperties(path,props);
121                         } catch (IOException JavaDoc ioe) {
122                             ErrorManager.getDefault().notify (ioe);
123                         }
124                     }
125                 }
126             });
127     }
128
129     /**
130      * In the case that the project is of current version or shared is false it delegates to
131      * AntProjectHelper.getPrimaryConfigurationData(), {@link AntProjectHelper#getPrimaryConfigurationData(boolean)}.
132      * Otherwise it creates an in memory update of shared configuration data and returns it.
133      * @param shared if true, refers to <code>project.xml</code>, else refers to
134      * <code>private.xml</code>
135      * @return the configuration data that is available
136      */

137     public Element JavaDoc getPrimaryConfigurationData (final boolean shared) {
138         return ProjectManager.mutex().readAccess(new Mutex.Action<Element JavaDoc>(){
139             public Element JavaDoc run() {
140                 if (!shared || isCurrent()) { //Only shared props should cause update
141
return helper.getPrimaryConfigurationData(shared);
142                 }
143                 else {
144                     return getUpdatedSharedConfigurationData ();
145                 }
146             }
147         });
148     }
149
150     /**
151      * In the case that the project is of current version or shared is false it calls AntProjectHelper.putPrimaryConfigurationData(),
152      * {@link AntProjectHelper#putPrimaryConfigurationData(Element, boolean)}.
153      * Otherwise it asks user to update the project. If the user agrees with the project update, it does the update and calls
154      * AntProjectHelper.PrimaryConfigurationData().
155      * @param element the configuration data
156      * @param shared if true, refers to <code>project.xml</code>, else refers to
157      * <code>private.xml</code>
158      */

159     public void putPrimaryConfigurationData (final Element JavaDoc element, final boolean shared) {
160         ProjectManager.mutex().writeAccess(new Runnable JavaDoc () {
161             public void run () {
162                 if (!shared || isCurrent()) {
163                     helper.putPrimaryConfigurationData(element, shared);
164                 } else if (canUpdate()) {
165                     try {
166                         saveUpdate ();
167                         helper.putPrimaryConfigurationData(element, shared);
168                     } catch (IOException JavaDoc ioe) {
169                         ErrorManager.getDefault().notify(ioe);
170                     }
171                 }
172             }
173         });
174     }
175
176     /**
177      * Returns an AntProjectHelper. The helper may not be used for accessing/storing project metadata.
178      * For project metadata manipulation the UpdateHelper must be used.
179      * @return AntProjectHelper
180      */

181     public AntProjectHelper getAntProjectHelper () {
182         return this.helper;
183     }
184
185     /**
186      * Request an saving of update. If the project is not of current version the user will be asked to update the project.
187      * If the user agrees with an update the project is updated.
188      * @return true if the metadata are of current version or updated
189      */

190     public boolean requestSave () throws IOException JavaDoc{
191         if (isCurrent()) {
192             return true;
193         }
194         if (!canUpdate()) {
195             return false;
196         }
197         saveUpdate ();
198         return true;
199     }
200
201     /**
202      * Returns true if the project is of current version.
203      * @return true if the project is of current version, otherwise false.
204      */

205     public synchronized boolean isCurrent () {
206         if (this.isCurrent == null) {
207             if ((this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/1",true) != null) ||
208                 (this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/2",true) != null)) {
209                 this.isCurrent = Boolean.FALSE;
210             } else {
211                 this.isCurrent = Boolean.TRUE;
212             }
213         }
214         return isCurrent.booleanValue();
215     }
216
217     private boolean canUpdate () {
218         if (TRANSPARENT_UPDATE) {
219             return true;
220         }
221         //Ask just once under a single write access
222
if (alreadyAskedInWriteAccess) {
223             return false;
224         }
225         else {
226             boolean canUpdate = this.notifier.canUpdate();
227             if (!canUpdate) {
228                 alreadyAskedInWriteAccess = true;
229                 ProjectManager.mutex().postReadRequest(new Runnable JavaDoc() {
230                     public void run() {
231                         alreadyAskedInWriteAccess = false;
232                     }
233                 });
234             }
235             return canUpdate;
236         }
237     }
238
239     private void saveUpdate () throws IOException JavaDoc {
240         this.helper.putPrimaryConfigurationData(getUpdatedSharedConfigurationData(),true);
241         this.cfg.removeConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/1",true); //NOI18N
242
this.cfg.removeConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/2",true); //NOI18N
243
ProjectManager.getDefault().saveProject (this.project);
244         synchronized(this) {
245             this.isCurrent = Boolean.TRUE;
246         }
247     }
248
249     private synchronized Element JavaDoc getUpdatedSharedConfigurationData () {
250         if (cachedElement == null) {
251             Element JavaDoc oldRoot = this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/1",true); //NOI18N
252
if (oldRoot != null) {
253                 Document JavaDoc doc = oldRoot.getOwnerDocument();
254                 Element JavaDoc newRoot = doc.createElementNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"data"); //NOI18N
255
copyDocument (doc, oldRoot, newRoot);
256                 Element JavaDoc sourceRoots = doc.createElementNS(J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"source-roots"); //NOI18N
257
Element JavaDoc root = doc.createElementNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"root"); //NOI18N
258
root.setAttribute ("id","src.dir"); //NOI18N
259
sourceRoots.appendChild(root);
260                 newRoot.appendChild (sourceRoots);
261                 Element JavaDoc testRoots = doc.createElementNS(J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"test-roots"); //NOI18N
262
root = doc.createElementNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"root"); //NOI18N
263
root.setAttribute ("id","test.src.dir"); //NOI18N
264
testRoots.appendChild (root);
265                 newRoot.appendChild (testRoots);
266                 cachedElement = updateMinAntVersion (newRoot, doc);
267             } else {
268                 oldRoot = this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/2",true); //NOI18N
269
if (oldRoot != null) {
270                     Document JavaDoc doc = oldRoot.getOwnerDocument();
271                     Element JavaDoc newRoot = doc.createElementNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"data"); //NOI18N
272
copyDocument (doc, oldRoot, newRoot);
273                     cachedElement = updateMinAntVersion (newRoot, doc);
274                 }
275             }
276         }
277         return cachedElement;
278     }
279     
280     private synchronized EditableProperties getUpdatedProjectProperties () {
281         EditableProperties cachedProperties = this.helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
282         //The javadoc.additionalparam was not in NB 4.0
283
if (cachedProperties.get (J2SEProjectProperties.JAVADOC_ADDITIONALPARAM)==null) {
284             cachedProperties.put (J2SEProjectProperties.JAVADOC_ADDITIONALPARAM,""); //NOI18N
285
}
286         if (cachedProperties.get ("build.generated.dir")==null) { //NOI18N
287
cachedProperties.put ("build.generated.dir","${build.dir}/generated"); //NOI18N
288
}
289          if (cachedProperties.get ("meta.inf.dir")==null) { //NOI18N
290
cachedProperties.put ("meta.inf.dir","${src.dir}/META-INF"); //NOI18N
291
}
292         return cachedProperties;
293     }
294
295     private static void copyDocument (Document JavaDoc doc, Element JavaDoc from, Element JavaDoc to) {
296         NodeList JavaDoc nl = from.getChildNodes();
297         int length = nl.getLength();
298         for (int i=0; i< length; i++) {
299             Node JavaDoc node = nl.item (i);
300             Node JavaDoc newNode = null;
301             switch (node.getNodeType()) {
302                 case Node.ELEMENT_NODE:
303                     Element JavaDoc oldElement = (Element JavaDoc) node;
304                     newNode = doc.createElementNS(J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,oldElement.getTagName());
305                     NamedNodeMap JavaDoc m = oldElement.getAttributes();
306                     Element JavaDoc newElement = (Element JavaDoc) newNode;
307                     for (int index = 0; index < m.getLength(); index++) {
308                         Node JavaDoc attr = m.item(index);
309                           newElement.setAttribute(attr.getNodeName(), attr.getNodeValue());
310                     }
311                     copyDocument(doc,oldElement,newElement);
312                     break;
313                 case Node.TEXT_NODE:
314                     Text JavaDoc oldText = (Text JavaDoc) node;
315                     newNode = doc.createTextNode(oldText.getData());
316                     break;
317                 case Node.COMMENT_NODE:
318                     Comment JavaDoc oldComment = (Comment JavaDoc) node;
319                     newNode = doc.createComment(oldComment.getData());
320                     break;
321             }
322             if (newNode != null) {
323                 to.appendChild (newNode);
324             }
325         }
326     }
327     
328     private static Element JavaDoc updateMinAntVersion (final Element JavaDoc root, final Document JavaDoc doc) {
329         NodeList JavaDoc list = root.getElementsByTagNameNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,MINIMUM_ANT_VERSION_ELEMENT);
330         if (list.getLength() == 1) {
331             Element JavaDoc me = (Element JavaDoc) list.item(0);
332             list = me.getChildNodes();
333             if (list.getLength() == 1) {
334                 me.replaceChild (doc.createTextNode(J2SEProjectGenerator.MINIMUM_ANT_VERSION), list.item(0));
335                 return root;
336             }
337         }
338         assert false : "Invalid project file"; //NOI18N
339
return root;
340     }
341
342     /**
343      * Creates an default Notifier. The default notifier displays a dialog warning user about project update.
344      * @return notifier
345      */

346     public static Notifier createDefaultNotifier () {
347         return new Notifier() {
348             public boolean canUpdate() {
349                 JButton JavaDoc updateOption = new JButton JavaDoc (NbBundle.getMessage(UpdateHelper.class, "CTL_UpdateOption"));
350                 updateOption.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(UpdateHelper.class, "AD_UpdateOption"));
351                 return DialogDisplayer.getDefault().notify(
352                     new NotifyDescriptor (NbBundle.getMessage(UpdateHelper.class,"TXT_ProjectUpdate", BUILD_NUMBER),
353                         NbBundle.getMessage(UpdateHelper.class,"TXT_ProjectUpdateTitle"),
354                         NotifyDescriptor.DEFAULT_OPTION,
355                         NotifyDescriptor.WARNING_MESSAGE,
356                         new Object JavaDoc[] {
357                             updateOption,
358                             NotifyDescriptor.CANCEL_OPTION
359                         },
360                         updateOption)) == updateOption;
361             }
362         };
363     }
364
365     /**
366      * Interface used by the UpdateHelper to ask user about
367      * the project update.
368      */

369     public static interface Notifier {
370         /**
371          * Asks user to update the project
372          * @return true if the project should be updated
373          */

374         public boolean canUpdate ();
375     }
376 }
377
Popular Tags