001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.apisupport.project;
043:
044: import java.io.BufferedReader;
045: import java.io.IOException;
046: import java.io.InputStream;
047: import java.io.InputStreamReader;
048: import java.io.OutputStream;
049: import java.io.OutputStreamWriter;
050: import java.io.PrintWriter;
051: import java.io.Reader;
052: import java.io.Writer;
053: import java.nio.charset.Charset;
054: import java.text.DateFormat;
055: import java.util.ArrayList;
056: import java.util.Arrays;
057: import java.util.Collections;
058: import java.util.Date;
059: import java.util.HashMap;
060: import java.util.Iterator;
061: import java.util.List;
062: import java.util.Map;
063: import java.util.Set;
064: import java.util.SortedSet;
065: import java.util.TreeSet;
066: import javax.script.ScriptContext;
067: import javax.script.ScriptEngine;
068: import javax.script.ScriptEngineManager;
069: import javax.script.ScriptException;
070: import javax.swing.text.PlainDocument;
071: import org.netbeans.api.project.Project;
072: import org.netbeans.api.project.ProjectManager;
073: import org.netbeans.api.queries.FileEncodingQuery;
074: import org.netbeans.modules.apisupport.project.layers.LayerUtils;
075: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
076: import org.netbeans.spi.project.support.ant.EditableProperties;
077: import org.netbeans.spi.project.support.ant.PropertyUtils;
078: import org.openide.ErrorManager;
079: import org.openide.cookies.SaveCookie;
080: import org.openide.filesystems.FileLock;
081: import org.openide.filesystems.FileObject;
082: import org.openide.filesystems.FileSystem;
083: import org.openide.filesystems.FileUtil;
084: import org.openide.loaders.CreateFromTemplateAttributesProvider;
085: import org.openide.loaders.DataObject;
086: import org.openide.loaders.DataObjectNotFoundException;
087: import org.openide.modules.SpecificationVersion;
088: import org.openide.text.IndentEngine;
089: import org.openide.util.Lookup;
090:
091: /**
092: * See javadoc in {@link CreatedModifiedFiles} for what this class and its
093: * methods is supposed to do.
094: */
095: public final class CreatedModifiedFilesFactory {
096:
097: private CreatedModifiedFilesFactory() {
098: }
099:
100: static CreatedModifiedFiles.Operation addLoaderSection(
101: Project project, String dataLoaderClass,
102: String installBefore) {
103: return new AddLoaderSection(project, dataLoaderClass,
104: installBefore);
105: }
106:
107: static CreatedModifiedFiles.Operation addLookupRegistration(
108: Project project, String interfaceClass, String implClass,
109: boolean inTests) {
110: return new AddLookupRegistration(project, interfaceClass,
111: implClass, inTests);
112: }
113:
114: static CreatedModifiedFiles.Operation addModuleDependency(
115: Project project, String codeNameBase,
116: String releaseVersion, SpecificationVersion version,
117: boolean useInCompiler) {
118: return new AddModuleDependency(project, codeNameBase,
119: releaseVersion, version, useInCompiler);
120: }
121:
122: static CreatedModifiedFiles.Operation bundleKey(Project project,
123: String key, String value, String bundlePath) {
124: return new BundleKey(project, key, value, bundlePath);
125: }
126:
127: static CreatedModifiedFiles.Operation bundleKeyDefaultBundle(
128: Project project, String key, String value) {
129: return new BundleKey(project, key, value);
130: }
131:
132: static CreatedModifiedFiles.Operation createFile(Project project,
133: String path, FileObject content) {
134: return new CreateFile(project, path, content);
135: }
136:
137: static CreatedModifiedFiles.Operation createFileWithSubstitutions(
138: Project project, String path, FileObject content,
139: Map<String, String> tokens) {
140: return new CreateFile(project, path, content, tokens);
141: }
142:
143: static CreatedModifiedFiles.Operation layerModifications(
144: Project project, CreatedModifiedFiles.LayerOperation op,
145: Set<String> externalFiles, CreatedModifiedFiles cmf) {
146: return new LayerModifications(project, op, externalFiles, cmf);
147: }
148:
149: static CreatedModifiedFiles.Operation createLayerEntry(
150: CreatedModifiedFiles cmf, Project project,
151: String layerPath, FileObject content,
152: Map<String, String> substitutionTokens,
153: String localizedDisplayName, Map<String, Object> attrs) {
154: return new CreateLayerEntry(cmf, project, layerPath, content,
155: substitutionTokens, localizedDisplayName, attrs);
156: }
157:
158: static CreatedModifiedFiles.Operation manifestModification(
159: Project project, String section,
160: Map<String, String> attributes) {
161: CreatedModifiedFilesFactory.ModifyManifest retval = new CreatedModifiedFilesFactory.ModifyManifest(
162: project);
163: for (Map.Entry<String, String> entry : attributes.entrySet()) {
164: retval.setAttribute(entry.getKey(), entry.getValue(),
165: section);
166: }
167: return retval;
168: }
169:
170: static CreatedModifiedFiles.Operation propertiesModification(
171: Project project, String propertyPath,
172: Map<String, String> properties) {
173: CreatedModifiedFilesFactory.ModifyProperties retval = new CreatedModifiedFilesFactory.ModifyProperties(
174: project, propertyPath);
175: for (Map.Entry<String, String> entry : properties.entrySet()) {
176: retval.setProperty(entry.getKey(), entry.getValue());
177: }
178: return retval;
179: }
180:
181: public static abstract class OperationBase implements
182: CreatedModifiedFiles.Operation {
183:
184: private Project project;
185: private SortedSet<String> createdPaths;
186: private SortedSet<String> modifiedPaths;
187: private SortedSet<String> invalidPaths;
188:
189: protected OperationBase(Project project) {
190: this .project = project;
191: }
192:
193: protected Project getProject() {
194: return project;
195: }
196:
197: protected NbModuleProvider getModuleInfo() {
198: return getProject().getLookup().lookup(
199: NbModuleProvider.class);
200: }
201:
202: public String[] getModifiedPaths() {
203: String[] s = new String[getModifiedPathsSet().size()];
204: return getModifiedPathsSet().toArray(s);
205: }
206:
207: public String[] getCreatedPaths() {
208: String[] s = new String[getCreatedPathsSet().size()];
209: return getCreatedPathsSet().toArray(s);
210: }
211:
212: public String[] getInvalidPaths() {
213: String[] s = new String[getInvalidPathsSet().size()];
214: return getInvalidPathsSet().toArray(s);
215:
216: }
217:
218: protected void addCreatedOrModifiedPath(String relPath,
219: boolean allowFileModification) {
220: // XXX this is probably wrong, since it might be created by an earlier op:
221: if (getProject().getProjectDirectory().getFileObject(
222: relPath) == null) {
223: getCreatedPathsSet().add(relPath);
224: } else {
225: if (allowFileModification) {
226: getModifiedPathsSet().add(relPath);
227: } else {
228: getInvalidPathsSet().add(relPath);
229: }
230: }
231: }
232:
233: protected void addPaths(CreatedModifiedFiles.Operation o) {
234: getCreatedPathsSet().addAll(
235: Arrays.asList(o.getCreatedPaths()));
236: getModifiedPathsSet().addAll(
237: Arrays.asList(o.getModifiedPaths()));
238: getInvalidPathsSet().addAll(
239: Arrays.asList(o.getInvalidPaths()));
240: }
241:
242: protected SortedSet<String> getCreatedPathsSet() {
243: if (createdPaths == null) {
244: createdPaths = new TreeSet<String>();
245: }
246: return createdPaths;
247: }
248:
249: protected SortedSet<String> getInvalidPathsSet() {
250: if (invalidPaths == null) {
251: invalidPaths = new TreeSet<String>();
252: }
253: return invalidPaths;
254: }
255:
256: protected SortedSet<String> getModifiedPathsSet() {
257: if (modifiedPaths == null) {
258: modifiedPaths = new TreeSet<String>();
259: }
260: return modifiedPaths;
261: }
262:
263: protected boolean addCreatedFileObject(FileObject fo) {
264: return getCreatedPathsSet().add(getProjectPath(fo));
265: }
266:
267: protected boolean addModifiedFileObject(FileObject fo) {
268: return getModifiedPathsSet().add(getProjectPath(fo));
269: }
270:
271: /**
272: * Doesn't check given arguments. Be sure they are valid as supposed by
273: * {@link PropertyUtils#relativizeFile(File, File)} method.
274: */
275: private String getProjectPath(FileObject file) {
276: return PropertyUtils.relativizeFile(FileUtil
277: .toFile(getProject().getProjectDirectory()),
278: FileUtil.normalizeFile(FileUtil.toFile(file)));
279: }
280:
281: }
282:
283: private static final class CreateFile extends OperationBase {
284:
285: private String path;
286: private FileObject content;
287: private Map<String, String> tokens;
288:
289: public CreateFile(Project project, String path,
290: FileObject content) {
291: this (project, path, content, null);
292: }
293:
294: public CreateFile(Project project, String path,
295: FileObject content, Map<String, String> tokens) {
296: super (project);
297: this .path = path;
298: if (content == null) {
299: throw new NullPointerException();
300: }
301: this .content = content;
302: this .tokens = tokens;
303: addCreatedOrModifiedPath(path, false);
304: }
305:
306: public void run() throws IOException {
307: FileObject target = FileUtil.createData(getProject()
308: .getProjectDirectory(), path);
309: if (tokens == null) {
310: copyByteAfterByte(content, target);
311: } else {
312: copyAndSubstituteTokens(content, target, tokens);
313: }
314: }
315:
316: }
317:
318: private static void copyByteAfterByte(FileObject content,
319: FileObject target) throws IOException {
320: OutputStream os = target.getOutputStream();
321: try {
322: InputStream is = content.getInputStream();
323: try {
324: FileUtil.copy(is, os);
325: } finally {
326: is.close();
327: }
328: } finally {
329: os.close();
330: }
331: }
332:
333: private static void copyAndSubstituteTokens(FileObject content,
334: FileObject target, Map<String, String> tokens)
335: throws IOException {
336: ScriptEngine engine = new ScriptEngineManager()
337: .getEngineByName("freemarker");
338: Map<String, Object> bindings = engine.getContext().getBindings(
339: ScriptContext.ENGINE_SCOPE);
340: String basename = target.getName();
341: for (CreateFromTemplateAttributesProvider provider : Lookup
342: .getDefault().lookupAll(
343: CreateFromTemplateAttributesProvider.class)) {
344: DataObject d = DataObject.find(content);
345: Map<String, ?> map = provider.attributesFor(d, d
346: .getFolder(), basename);
347: if (map != null) {
348: bindings.putAll(map);
349: }
350: }
351: bindings.put("name", basename.replaceFirst("\\.[^./]+$", "")); // NOI18N
352: bindings.put("user", System.getProperty("user.name")); // NOI18N
353: Date d = new Date();
354: bindings.put("date", DateFormat.getDateInstance().format(d)); // NOI18N
355: bindings.put("time", DateFormat.getTimeInstance().format(d)); // NOI18N
356: bindings.put("nameAndExt", target.getNameExt()); // NOI18N
357: bindings.putAll(tokens);
358: Charset targetEnc = FileEncodingQuery.getEncoding(target);
359: Charset sourceEnc = FileEncodingQuery.getEncoding(content);
360: bindings.put("encoding", targetEnc.name());
361: Writer w = new OutputStreamWriter(target.getOutputStream(),
362: targetEnc);
363: try {
364: IndentEngine format = IndentEngine.find(content
365: .getMIMEType());
366: if (format != null) {
367: PlainDocument doc = new PlainDocument();
368: doc.putProperty(
369: PlainDocument.StreamDescriptionProperty,
370: content);
371: w = format.createWriter(doc, 0, w);
372: }
373: engine.getContext().setWriter(w);
374: engine.getContext().setAttribute(
375: FileObject.class.getName(), content,
376: ScriptContext.ENGINE_SCOPE);
377: engine.getContext().setAttribute(ScriptEngine.FILENAME,
378: content.getNameExt(), ScriptContext.ENGINE_SCOPE);
379: Reader is = new InputStreamReader(content.getInputStream(),
380: sourceEnc);
381: try {
382: engine.eval(is);
383: } catch (ScriptException x) {
384: throw (IOException) new IOException(x.toString())
385: .initCause(x);
386: } finally {
387: is.close();
388: }
389: } finally {
390: w.close();
391: }
392: }
393:
394: private static final class BundleKey extends OperationBase {
395:
396: private final String bundlePath;
397: private final String key;
398: private final String value;
399:
400: public BundleKey(Project project, String key, String value) {
401: this (project, key, value, null);
402: }
403:
404: public BundleKey(Project project, String key, String value,
405: String bundlePath) {
406: super (project);
407: this .key = key;
408: this .value = value;
409: if (bundlePath == null) {
410:
411: ManifestManager mm = ManifestManager.getInstance(
412: Util.getManifest(getModuleInfo()
413: .getManifestFile()), false);
414: String srcDir = getModuleInfo()
415: .getResourceDirectoryPath(false);
416: this .bundlePath = srcDir + "/"
417: + mm.getLocalizingBundle(); // NOI18N
418: } else {
419: this .bundlePath = bundlePath;
420: }
421: addCreatedOrModifiedPath(this .bundlePath, true);
422: }
423:
424: public void run() throws IOException {
425: FileObject prjDir = getProject().getProjectDirectory();
426: FileObject bundleFO = FileUtil.createData(prjDir,
427: bundlePath);
428: EditableProperties ep = Util.loadProperties(bundleFO);
429: ep.setProperty(key, value);
430: Util.storeProperties(bundleFO, ep);
431: }
432:
433: }
434:
435: private static final class AddLoaderSection extends OperationBase {
436:
437: private FileObject mfFO;
438:
439: private String dataLoaderClass;
440: private String installBefore;
441:
442: public AddLoaderSection(Project project,
443: String dataLoaderClass, String installBefore) {
444: super (project);
445: this .dataLoaderClass = dataLoaderClass + ".class"; // NOI18N
446: this .installBefore = installBefore;
447: this .mfFO = getModuleInfo().getManifestFile();
448: addModifiedFileObject(mfFO);
449: }
450:
451: public void run() throws IOException {
452: //#65420 it can happen the manifest is currently being edited. save it
453: // and cross fingers because it can be in inconsistent state
454: try {
455: DataObject dobj = DataObject.find(mfFO);
456: SaveCookie safe = dobj.getCookie(SaveCookie.class);
457: if (safe != null) {
458: safe.save();
459: }
460: } catch (DataObjectNotFoundException ex) {
461: Util.err.notify(ErrorManager.WARNING, ex);
462: }
463:
464: EditableManifest em = Util.loadManifest(mfFO);
465: em.addSection(dataLoaderClass);
466: em.setAttribute("OpenIDE-Module-Class", "Loader",
467: dataLoaderClass); // NOI18N
468: if (installBefore != null) {
469: em.setAttribute("Install-Before", installBefore,
470: dataLoaderClass); //NOI18N
471: }
472: Util.storeManifest(mfFO, em);
473: }
474:
475: }
476:
477: private static final class AddModuleDependency extends
478: OperationBase {
479:
480: private String codeNameBase;
481: private String releaseVersion;
482: private SpecificationVersion specVersion;
483: private boolean useInCompiler;
484:
485: public AddModuleDependency(Project project,
486: String codeNameBase, String releaseVersion,
487: SpecificationVersion specVersion, boolean useInCompiler) {
488: super (project);
489: this .codeNameBase = codeNameBase;
490: this .releaseVersion = releaseVersion;
491: this .specVersion = specVersion;
492: this .useInCompiler = useInCompiler;
493: getModifiedPathsSet().add(
494: getModuleInfo().getProjectFilePath()); // NOI18N
495: }
496:
497: public void run() throws IOException {
498: getModuleInfo().addDependency(codeNameBase, releaseVersion,
499: specVersion, useInCompiler);
500: // XXX consider this carefully
501: ProjectManager.getDefault().saveProject(getProject());
502: }
503:
504: }
505:
506: private static final class AddLookupRegistration extends
507: OperationBase {
508:
509: private String interfaceClassPath;
510: private String implClass;
511:
512: public AddLookupRegistration(Project project,
513: String interfaceClass, String implClass, boolean inTests) {
514: super (project);
515: this .implClass = implClass;
516: this .interfaceClassPath = getModuleInfo()
517: .getResourceDirectoryPath(inTests)
518: + // NOI18N
519: "/META-INF/services/" + interfaceClass; // NOI18N
520: addCreatedOrModifiedPath(interfaceClassPath, true);
521: }
522:
523: public void run() throws IOException {
524: FileObject service = FileUtil.createData(getProject()
525: .getProjectDirectory(), interfaceClassPath);
526:
527: List<String> lines = new ArrayList<String>();
528: InputStream serviceIS = service.getInputStream();
529: try {
530: BufferedReader br = new BufferedReader(
531: new InputStreamReader(serviceIS, "UTF-8")); // NOI18N
532: String line;
533: while ((line = br.readLine()) != null) {
534: lines.add(line);
535: }
536: } finally {
537: serviceIS.close();
538: }
539:
540: FileLock lock = service.lock();
541: try {
542: PrintWriter pw = new PrintWriter(
543: new OutputStreamWriter(service
544: .getOutputStream(lock), "UTF-8")); // NOI18N
545: try {
546: Iterator<String> it = lines.iterator();
547: while (it.hasNext()) {
548: String line = it.next();
549: if (it.hasNext() || !line.trim().equals("")) {
550: pw.println(line);
551: }
552: }
553: pw.println(implClass);
554: } finally {
555: pw.close();
556: }
557: } finally {
558: lock.releaseLock();
559: }
560:
561: }
562: }
563:
564: private static final class CreateLayerEntry extends OperationBase {
565:
566: private final CreatedModifiedFiles.Operation createBundleKey;
567: private final CreatedModifiedFiles.Operation layerOp;
568:
569: public CreateLayerEntry(final CreatedModifiedFiles cmf,
570: final Project project, final String layerPath,
571: final FileObject content,
572: final Map<String, String> tokens,
573: final String localizedDisplayName,
574: final Map<String, Object> attrs) {
575:
576: super (project);
577: CreatedModifiedFiles.LayerOperation op = new CreatedModifiedFiles.LayerOperation() {
578: public void run(FileSystem layer) throws IOException {
579: FileObject targetFO = FileUtil.createData(layer
580: .getRoot(), layerPath);
581: if (content != null) {
582: if (tokens == null) {
583: copyByteAfterByte(content, targetFO);
584: } else {
585: copyAndSubstituteTokens(content, targetFO,
586: tokens);
587: }
588: }
589: if (localizedDisplayName != null) {
590: String bundlePath = ManifestManager
591: .getInstance(
592: Util
593: .getManifest(getModuleInfo()
594: .getManifestFile()),
595: false).getLocalizingBundle();
596: String suffix = ".properties"; // NOI18N
597: if (bundlePath != null
598: && bundlePath.endsWith(suffix)) {
599: String name = bundlePath.substring(
600: 0,
601: bundlePath.length()
602: - suffix.length()).replace(
603: '/', '.');
604: targetFO
605: .setAttribute(
606: "SystemFileSystem.localizingBundle",
607: name); // NOI18N
608: } else {
609: // XXX what?
610: }
611: }
612: if (attrs != null) {
613: for (Map.Entry<String, Object> entry : attrs
614: .entrySet()) {
615: targetFO.setAttribute(entry.getKey(), entry
616: .getValue());
617: }
618: }
619: }
620: };
621: Set<String> externalFiles;
622: if (content != null) {
623: FileObject xml = LayerUtils.layerForProject(project)
624: .getLayerFile();
625: FileObject parent = xml != null ? xml.getParent()
626: : null;
627: // XXX this is not fully accurate since if two ops would both create the same file,
628: // really the second one would automatically generate a uniquified name... but close enough!
629: externalFiles = Collections.singleton(LayerUtils
630: .findGeneratedName(parent, layerPath));
631: } else {
632: externalFiles = Collections.emptySet();
633: }
634: FileSystem layer = cmf.getLayerHandle().layer(false);
635: if (layer != null && layer.findResource(layerPath) != null) {
636: layerOp = new CreatedModifiedFiles.Operation() {
637: public void run() throws IOException {
638: throw new IOException("cannot run"); // NOI18N
639: }
640:
641: public String[] getModifiedPaths() {
642: return new String[0];
643: }
644:
645: public String[] getCreatedPaths() {
646: return new String[0];
647: }
648:
649: public String[] getInvalidPaths() {
650: // #85138: make sure we do not overwrite an existing entry.
651: return new String[] { layerPath };
652: }
653: };
654: } else {
655: layerOp = new LayerModifications(project, op,
656: externalFiles, cmf);
657: }
658: addPaths(layerOp);
659: if (localizedDisplayName != null) {
660: this .createBundleKey = new BundleKey(getProject(),
661: layerPath, localizedDisplayName);
662: addPaths(this .createBundleKey);
663: } else {
664: createBundleKey = null;
665: }
666: }
667:
668: public void run() throws IOException {
669: layerOp.run();
670: if (createBundleKey != null) {
671: createBundleKey.run();
672: }
673: }
674: }
675:
676: private static final class LayerModifications implements
677: CreatedModifiedFiles.Operation {
678:
679: private final Project project;
680: private final CreatedModifiedFiles.LayerOperation op;
681: private final Set<String> externalFiles;
682: private final CreatedModifiedFiles cmf;
683:
684: public LayerModifications(Project project,
685: CreatedModifiedFiles.LayerOperation op,
686: Set<String> externalFiles, CreatedModifiedFiles cmf) {
687: this .project = project;
688: this .op = op;
689: this .externalFiles = externalFiles;
690: this .cmf = cmf;
691: }
692:
693: public void run() throws IOException {
694: op.run(cmf.getLayerHandle().layer(true));
695: }
696:
697: private String layerPrefix() {
698: FileObject layer = cmf.getLayerHandle().getLayerFile();
699: if (layer == null) {
700: return null;
701: }
702: return FileUtil.getRelativePath(project
703: .getProjectDirectory(), layer);
704: }
705:
706: public String[] getModifiedPaths() {
707: String layerPath = layerPrefix();
708: if (layerPath == null) {
709: return new String[0];
710: }
711: return new String[] { layerPath };
712: }
713:
714: public String[] getCreatedPaths() {
715: String layerPath = layerPrefix();
716: if (layerPath == null) {
717: return new String[0];
718: }
719: int slash = layerPath.lastIndexOf('/');
720: String prefix = layerPath.substring(0, slash + 1);
721: SortedSet<String> s = new TreeSet<String>();
722: for (String file : externalFiles) {
723: s.add(prefix + file);
724: }
725: return s.toArray(new String[s.size()]);
726: }
727:
728: public String[] getInvalidPaths() {
729: //TODO applicable here?
730: return new String[0];
731: }
732:
733: }
734:
735: /**
736: * Operation for making changes in manifest
737: */
738: public static class ModifyManifest extends
739: CreatedModifiedFilesFactory.OperationBase {
740: private FileObject manifestFile;
741: private Map<String, Map<String, String>> attributesToAdd;
742:
743: /**
744: * @param project
745: */
746: public ModifyManifest(final Project project) {
747: super (project);
748: this .attributesToAdd = new HashMap<String, Map<String, String>>();
749: addModifiedFileObject(getManifestFile());
750: }
751:
752: /**
753: * Adds requirement for modifying attribute for the main section. How attribute
754: * will be modified depends on implementation of method {@link performModification}.
755: * @param name the attribute name
756: * @param value the new attribute value
757: */
758: public final void setAttribute(final String name,
759: final String value) {
760: setAttribute(name, value, "null");//NOI18N
761: }
762:
763: /**
764: * Adds requirement for modifying attribute. How attribute
765: * will be modified depends on implementation of method {@link performModification}.
766: * @param name the attribute name
767: * @param value the new attribute value
768: * @param section the name of the section or null for the main section
769: */
770: public final void setAttribute(final String name,
771: final String value, final String section) {
772: Map<String, String> attribs = attributesToAdd.get(section);
773: if (attribs == null) {
774: attribs = new HashMap<String, String>();
775: attributesToAdd.put(section, attribs);
776: }
777: attribs.put(name, value);
778: }
779:
780: /**
781: * Creates section if doesn't exists and set all attributes
782: * @param em EditableManifest where attribute represented by other
783: * parameters is going to be added
784: * @param name the attribute name
785: * @param value the new attribute value
786: * @param section the name of the section to add it to, or null for the main section
787: */
788: protected void performModification(final EditableManifest em,
789: final String name, final String value,
790: final String section) {
791: if (section != null
792: && em.getSectionNames().contains(section)) {
793: em.addSection(section);
794: }
795: em.setAttribute(name, value, section);
796: }
797:
798: public final void run() throws IOException {
799: ensureSavingFirst();
800:
801: EditableManifest em = Util.loadManifest(getManifestFile());
802: for (Map.Entry<String, Map<String, String>> entry : attributesToAdd
803: .entrySet()) {
804: String section = entry.getKey();
805: for (Map.Entry<String, String> subentry : entry
806: .getValue().entrySet()) {
807: performModification(em, subentry.getKey(), subentry
808: .getValue(),
809: (("null".equals(section)) ? null : section)); // NOI18N
810: }
811: }
812:
813: Util.storeManifest(getManifestFile(), em);
814: }
815:
816: private FileObject getManifestFile() {
817: if (manifestFile == null) {
818: manifestFile = getModuleInfo().getManifestFile();
819: }
820: return manifestFile;
821: }
822:
823: private void ensureSavingFirst() throws IOException {
824: //#65420 it can happen the manifest is currently being edited. save it
825: // and cross fingers because it can be in inconsistent state
826: try {
827: DataObject dobj = DataObject.find(getManifestFile());
828: SaveCookie safe = dobj.getCookie(SaveCookie.class);
829: if (safe != null) {
830: safe.save();
831: }
832: } catch (DataObjectNotFoundException ex) {
833: Util.err.notify(ErrorManager.WARNING, ex);
834: }
835: }
836: }
837:
838: /**
839: * Operation for making changes in properties
840: */
841: private static class ModifyProperties extends
842: CreatedModifiedFilesFactory.OperationBase {
843: private Map<String, String> properties;
844: private final String propertyPath;
845: private EditableProperties ep;
846: private FileObject propertiesFile;
847:
848: private ModifyProperties(final Project project,
849: final String propertyPath) {
850: super (project);
851: this .propertyPath = propertyPath;
852: addCreatedOrModifiedPath(propertyPath, true);
853: }
854:
855: public void run() throws IOException {
856: EditableProperties p = getEditableProperties();
857: p.putAll(getProperties());
858: Util.storeProperties(getPropertyFile(), p);
859: }
860:
861: public final void setProperty(final String name,
862: final String value) {
863: getProperties().put(name, value);
864: }
865:
866: protected final FileObject getPropertyFile() throws IOException {
867: if (propertiesFile == null) {
868: FileObject projectDirectory = getProject()
869: .getProjectDirectory();
870: propertiesFile = FileUtil.createData(projectDirectory,
871: propertyPath);
872: }
873: return propertiesFile;
874: }
875:
876: protected final EditableProperties getEditableProperties()
877: throws IOException {
878: if (ep == null) {
879: ep = Util.loadProperties(getPropertyFile());
880: }
881: return ep;
882: }
883:
884: protected final Map<String, String> getProperties() {
885: if (properties == null) {
886: this .properties = new HashMap<String, String>();
887: }
888: return properties;
889: }
890: }
891: }
|