KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > j2ee > clientproject > 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.j2ee.clientproject;
21
22 import java.io.IOException JavaDoc;
23 import javax.swing.JButton JavaDoc;
24 import org.netbeans.modules.j2ee.clientproject.api.AppClientProjectGenerator;
25 import org.netbeans.modules.j2ee.clientproject.ui.customizer.AppClientProjectProperties;
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.modules.websvc.api.jaxws.project.GeneratedFilesHelper;
41 import org.netbeans.spi.project.AuxiliaryConfiguration;
42 import org.netbeans.spi.project.support.ant.AntProjectHelper;
43 import org.netbeans.spi.project.support.ant.EditableProperties;
44
45
46 /**
47  * Proxy for the AntProjectHelper which defers the update of the project metadata
48  * to explicit user action. Currently it is hard coded for update from
49  * "http://www.netbeans.org/ns/j2se-project/1" to "http://www.netbeans.org/ns/j2se-project/2".
50  * In future it should define plugable SPI.
51  */

52 public class UpdateHelper {
53
54     private static final boolean TRANSPARENT_UPDATE = Boolean.getBoolean("carproject.transparentUpdate");
55     private static final String JavaDoc BUILD_NUMBER = System.getProperty("netbeans.buildnumber"); // NOI18N
56
private static final String JavaDoc MINIMUM_ANT_VERSION_ELEMENT = "minimum-ant-version"; // NOI18N
57

58     private final Project project;
59     private final AntProjectHelper helper;
60     private final AuxiliaryConfiguration cfg;
61     private final GeneratedFilesHelper genFileHelper;
62     private final Notifier notifier;
63     private boolean alreadyAskedInWriteAccess;
64     private Boolean JavaDoc isCurrent;
65     private Element JavaDoc cachedElement;
66
67     /**
68      * Creates new UpdateHelper
69      * @param project
70      * @param helper AntProjectHelper
71      * @param cfg AuxiliaryConfiguration
72      * @param genFileHelper GeneratedFilesHelper
73      * @param notifier used to ask user about project update
74      */

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

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

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

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

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

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

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

206     public synchronized boolean isCurrent () {
207         /*
208         if (this.isCurrent == null) {
209             if ((this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/1",true) != null) ||
210                 (this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/j2se-project/2",true) != null)) {
211                 this.isCurrent = Boolean.FALSE;
212             } else {
213                 this.isCurrent = Boolean.TRUE;
214             }
215         }
216         return isCurrent.booleanValue();
217          */

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

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

374     public static interface Notifier {
375         /**
376          * Asks user to update the project
377          * @return true if the project should be updated
378          */

379         public boolean canUpdate ();
380     }
381 }
382
Popular Tags