KickJava   Java API By Example, From Geeks To Geeks.

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

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

323     private static Element JavaDoc updateMinAntVersion (final Element JavaDoc root, final Document JavaDoc doc) {
324         NodeList JavaDoc list = root.getElementsByTagNameNS (BluejProjectType.PROJECT_CONFIGURATION_NAMESPACE,MINIMUM_ANT_VERSION_ELEMENT);
325         if (list.getLength() == 1) {
326             Element JavaDoc me = (Element JavaDoc) list.item(0);
327             list = me.getChildNodes();
328             if (list.getLength() == 1) {
329                 me.replaceChild (doc.createTextNode(MINIMUM_ANT_VERSION), list.item(0));
330                 return root;
331             }
332         }
333         assert false : "Invalid project file"; // NOI18N
334
return root;
335     }
336
337     /**
338      * Creates an default Notifier. The default notifier displays a dialog warning user about project update.
339      * @return notifier
340      */

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

364     public static interface Notifier {
365         /**
366          * Asks user to update the project
367          * @return true if the project should be updated
368          */

369         public boolean canUpdate ();
370     }
371 }
372
Popular Tags