001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM - Initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.build;
011:
012: import java.io.*;
013: import java.util.*;
014: import org.eclipse.ant.core.AntRunner;
015: import org.eclipse.core.runtime.*;
016: import org.eclipse.osgi.util.NLS;
017: import org.eclipse.pde.build.Constants;
018: import org.eclipse.pde.build.IFetchFactory;
019: import org.eclipse.pde.internal.build.ant.AntScript;
020: import org.eclipse.pde.internal.build.fetch.CVSFetchTaskFactory;
021: import org.eclipse.update.core.*;
022: import org.eclipse.update.internal.core.FeatureExecutableFactory;
023: import org.osgi.framework.Version;
024:
025: /**
026: * Generates Ant scripts with a repository specific factory
027: * to retrieve plug-ins and features from a repository.
028: */
029: public class FetchScriptGenerator extends AbstractScriptGenerator {
030: private static final String FETCH_TASK_FACTORY = "internal.factory"; //$NON-NLS-1$
031: private static final String MATCHED_VERSION = "internal.matchedVersion"; //$NON-NLS-1$
032:
033: // flag saying if we want to recursively generate the scripts
034: protected boolean recursiveGeneration = true;
035:
036: // Points to the map files containing references to repository
037: protected Properties directoryFile;
038: protected String directoryLocation;
039: protected SortedMap directory;
040:
041: // The location of the CVS password file.
042: protected String cvsPassFileLocation;
043:
044: protected boolean fetchChildren = true;
045:
046: protected Properties fetchTags = null;
047:
048: // The element (an entry of the map file) for which we create the script
049: protected String element;
050: protected Version elementVersion;
051:
052: // The feature object representing the element
053: protected IFeature feature;
054: //The map infos (of the element) processed
055: protected Map mapInfos;
056: // The content of the build.properties file associated with the feature
057: protected Properties featureProperties;
058: // Variables to control is a mkdir to a specific folder was already.
059: protected List mkdirLocations = new ArrayList(5);
060: // A property table containing the association between the plugins and the version from the map
061: protected Properties repositoryPluginTags = new Properties();
062: protected Properties repositoryFeatureTags = new Properties();
063:
064: //The registry of the task factories
065: private FetchTaskFactoriesRegistry fetchTaskFactories;
066: //Set of all the used factories while generating the fetch script for the top level element
067: private Set encounteredTypeOfRepo = new HashSet();
068:
069: public static final String FEATURE_ONLY = "featureOnly"; //$NON-NLS-1$
070: public static final String FEATURE_AND_PLUGINS = "featureAndPlugins"; //$NON-NLS-1$
071: public static final String FEATURES_RECURSIVELY = "featuresRecursively"; //$NON-NLS-1$
072: public static final String FETCH_FILE_PREFIX = "fetch_"; //$NON-NLS-1$
073:
074: private String scriptName;
075:
076: public FetchScriptGenerator() {
077: super ();
078: }
079:
080: public FetchScriptGenerator(String element) {
081: setElement(element);
082: }
083:
084: public void setElement(String element) {
085: Object[] split = splitElement(element);
086: this .element = (String) split[0];
087: this .elementVersion = (Version) split[1];
088: }
089:
090: private Object[] splitElement(String elt) {
091: int comma = elt.indexOf(',');
092: if (comma == -1) {
093: return new Object[] { elt, Version.emptyVersion };
094: }
095: return new Object[] { elt.substring(0, comma),
096: new Version(elt.substring(comma + 1)) };
097: }
098:
099: private void initializeFactories() {
100: fetchTaskFactories = new FetchTaskFactoriesRegistry();
101: }
102:
103: /**
104: * @see AbstractScriptGenerator#generate()
105: */
106: public void generate() throws CoreException {
107: initializeFactories();
108: mapInfos = processMapFileEntry(element, elementVersion);
109: if (mapInfos == null) {
110: IStatus warning = new Status(IStatus.WARNING, PI_PDEBUILD,
111: WARNING_ELEMENT_NOT_FETCHED, NLS.bind(
112: Messages.error_fetchingFailed, element),
113: null);
114: BundleHelper.getDefault().getLog().log(warning);
115: return;
116: }
117:
118: scriptName = FETCH_FILE_PREFIX
119: + mapInfos.get(IFetchFactory.KEY_ELEMENT_NAME) + ".xml"; //$NON-NLS-1$
120: openScript(workingDirectory, scriptName);
121: try {
122: generateFetchScript();
123: } finally {
124: closeScript();
125: }
126:
127: if (recursiveGeneration
128: && mapInfos.get(IFetchFactory.KEY_ELEMENT_TYPE).equals(
129: IFetchFactory.ELEMENT_TYPE_FEATURE))
130: generateFetchFilesForIncludedFeatures();
131:
132: saveRepositoryTags();
133: }
134:
135: private void saveRepositoryTags(Properties properties,
136: String fileName) throws CoreException {
137: try {
138: InputStream input = new BufferedInputStream(
139: new FileInputStream(workingDirectory + '/'
140: + fileName));
141: try {
142: properties.load(input);
143: } finally {
144: input.close();
145: }
146: } catch (IOException e) {
147: //ignore the exception, the same may not exist
148: }
149:
150: try {
151: OutputStream os = new BufferedOutputStream(
152: new FileOutputStream(workingDirectory + '/'
153: + fileName));
154: try {
155: properties.store(os, null);
156: } finally {
157: os.close();
158: }
159: } catch (IOException e) {
160: String message = NLS.bind(Messages.exception_writingFile,
161: workingDirectory + '/' + fileName);
162: throw new CoreException(new Status(IStatus.ERROR,
163: PI_PDEBUILD, EXCEPTION_WRITING_FILE, message, null));
164: }
165: }
166:
167: private void saveRepositoryTags() throws CoreException {
168: saveRepositoryTags(repositoryPluginTags,
169: DEFAULT_PLUGIN_REPOTAG_FILENAME_DESCRIPTOR);
170: saveRepositoryTags(repositoryFeatureTags,
171: DEFAULT_FEATURE_REPOTAG_FILENAME_DESCRIPTOR);
172: }
173:
174: /**
175: * Method generateFetchFilesForRequiredFeatures.
176: */
177: private void generateFetchFilesForIncludedFeatures()
178: throws CoreException {
179: IIncludedFeatureReference[] referencedFeatures = ((Feature) feature)
180: .getFeatureIncluded();
181: for (int i = 0; i < referencedFeatures.length; i++) {
182: String featureId = referencedFeatures[i]
183: .getVersionedIdentifier().getIdentifier();
184: if (featureProperties
185: .containsKey(GENERATION_SOURCE_FEATURE_PREFIX
186: + featureId))
187: continue;
188:
189: FetchScriptGenerator generator = new FetchScriptGenerator(
190: "feature@" + featureId + ',' + referencedFeatures[i].getVersionedIdentifier().getVersion().toString()); //$NON-NLS-1$
191: generator.setDirectoryLocation(directoryLocation);
192: generator.setFetchChildren(fetchChildren);
193: generator.setCvsPassFileLocation(cvsPassFileLocation);
194: generator.setRecursiveGeneration(recursiveGeneration);
195: generator.setFetchTag(fetchTags);
196: generator.setDirectory(directory);
197: generator.setDirectoryFile(directoryFile);
198: generator.setBuildSiteFactory(siteFactory);
199: generator.repositoryPluginTags = repositoryPluginTags;
200: generator.generate();
201: }
202: }
203:
204: /**
205: * Main call for generating the script.
206: *
207: * @throws CoreException
208: */
209: protected void generateFetchScript() throws CoreException {
210: generatePrologue();
211: generateFetchTarget();
212: generateFetchElementTarget();
213: if (mapInfos.get(IFetchFactory.KEY_ELEMENT_TYPE).equals(
214: IFetchFactory.ELEMENT_TYPE_FEATURE)) {
215: generateFetchPluginsTarget();
216: generateFetchRecusivelyTarget();
217: }
218: generateAdditionalTargets();
219: generateEpilogue();
220: }
221:
222: protected void generateFetchTarget() {
223: //CONDITION Here we could check the values contained in the header of the file.
224: // However it will require to have either generic value or to be sure that the value can be omitted
225: // This would be necessary for the top level feature
226: script.println();
227: script.printTargetDeclaration(TARGET_FETCH, null, null, null,
228: null);
229: //don't do a fetch element for the generated container feature
230: if (!mapInfos.get(IFetchFactory.KEY_ELEMENT_NAME).equals(
231: IPDEBuildConstants.CONTAINER_FEATURE))
232: script.printAntCallTask(TARGET_FETCH_ELEMENT, true, null);
233: if (mapInfos.get(IFetchFactory.KEY_ELEMENT_TYPE).equals(
234: IFetchFactory.ELEMENT_TYPE_FEATURE)) {
235: script.printAntCallTask(TARGET_FETCH_PLUGINS, true, null);
236: script.printAntCallTask(TARGET_FETCH_RECURSIVELY, true,
237: null);
238: }
239: script.printTargetEnd();
240: }
241:
242: protected void generateFetchElementTarget() {
243: //don't try to fetch a generated container feature
244: if (mapInfos.get(IFetchFactory.KEY_ELEMENT_NAME).equals(
245: IPDEBuildConstants.CONTAINER_FEATURE))
246: return;
247: script.printTargetDeclaration(TARGET_FETCH_ELEMENT, null,
248: FEATURE_ONLY, null, null);
249: try {
250: generateFetchEntry(element, elementVersion, false);
251: } catch (CoreException e) {
252: IStatus status = new Status(IStatus.ERROR, PI_PDEBUILD,
253: WARNING_ELEMENT_NOT_FETCHED, NLS.bind(
254: Messages.error_fetchingFailed, element),
255: null);
256: BundleHelper.getDefault().getLog().log(status);
257: }
258: script.printTargetEnd();
259: }
260:
261: protected void generateFetchPluginsTarget() throws CoreException {
262: script.printTargetDeclaration(TARGET_FETCH_PLUGINS, null,
263: FEATURE_AND_PLUGINS, null, null);
264: retrieveFeature((String) mapInfos
265: .get(IFetchFactory.KEY_ELEMENT_NAME), (String) mapInfos
266: .get(IFetchFactory.KEY_ELEMENT_TYPE), mapInfos);
267: generateChildrenFetchScript();
268: script.printTargetEnd();
269: }
270:
271: /**
272: * Decompose the elements constituting a Map file entry. The values are returned
273: * in a Map. <code>null</code> is returned if the entry does not exist.
274: * @param entry
275: * @return Map
276: * @throws CoreException
277: */
278: private Map processMapFileEntry(String entry, Version version)
279: throws CoreException {
280: Map entryInfos = new HashMap(5);
281:
282: // extract type and element from entry
283: int index = entry.indexOf('@');
284: String type = entry.substring(0, index);
285: String currentElement = entry.substring(index + 1);
286:
287: // read and validate the repository info for the entry
288: Object[] match = getRepositoryInfo(entry, version);
289: String repositoryInfo = match == null ? null
290: : (String) match[0];
291: if (repositoryInfo == null) {
292: if (IPDEBuildConstants.CONTAINER_FEATURE
293: .equals(currentElement)) {
294: entryInfos.put(IFetchFactory.KEY_ELEMENT_TYPE, type);
295: entryInfos.put(IFetchFactory.KEY_ELEMENT_NAME,
296: currentElement);
297: return entryInfos;
298: }
299: String message = NLS.bind(
300: Messages.error_missingDirectoryEntry,
301: Version.emptyVersion.equals(version) ? entry
302: : entry + ',' + version.toString());
303: BundleHelper.getDefault().getLog().log(
304: new Status(IStatus.ERROR, PI_PDEBUILD,
305: EXCEPTION_ENTRY_MISSING, message, null));
306: return null;
307: }
308:
309: // Get the repo identifier
310: int idx = repositoryInfo.indexOf(',');
311: if (idx == -1) {
312: String message = NLS.bind(
313: Messages.error_incorrectDirectoryEntry,
314: currentElement);
315: throw new CoreException(
316: new Status(IStatus.ERROR, PI_PDEBUILD,
317: EXCEPTION_ENTRY_MISSING, message, null));
318: }
319: String repoIdentifier = repositoryInfo.substring(0, idx).trim();
320:
321: // Get the repo factory corresponding
322: IFetchFactory fetchTaskFactory = null;
323: String repoSpecificSegment = null;
324: // if the type can not be found it is probably because it is an old style map file
325: if (!fetchTaskFactories.getFactoryIds()
326: .contains(repoIdentifier)) {
327: repoIdentifier = CVSFetchTaskFactory.ID;
328: repoSpecificSegment = repositoryInfo;
329: } else {
330: repoSpecificSegment = repositoryInfo.substring(idx + 1,
331: repositoryInfo.length()); //TODO Need to see if we can go out idx + 1
332: }
333:
334: fetchTaskFactory = fetchTaskFactories
335: .getFactory(repoIdentifier);
336: if (fetchTaskFactory == null) {
337: String message = NLS.bind(
338: Messages.error_noCorrespondingFactory,
339: currentElement);
340: throw new CoreException(
341: new Status(IStatus.ERROR, PI_PDEBUILD,
342: EXCEPTION_ENTRY_MISSING, message, null));
343: }
344:
345: encounteredTypeOfRepo.add(fetchTaskFactory);
346: // add general infos (will override builder specific infos)
347: entryInfos.put(IFetchFactory.KEY_ELEMENT_TYPE, type);
348: entryInfos.put(IFetchFactory.KEY_ELEMENT_NAME, currentElement);
349:
350: // add infos from registered builder
351: fetchTaskFactory.parseMapFileEntry(repoSpecificSegment,
352: fetchTags, entryInfos);
353:
354: // store builder
355: entryInfos.put(FETCH_TASK_FACTORY, fetchTaskFactory);
356:
357: // keep track of the version of the element as found in the map file
358: entryInfos.put(MATCHED_VERSION, match[1]);
359: return entryInfos;
360: }
361:
362: protected void generateFetchRecusivelyTarget() throws CoreException {
363: script.printTargetDeclaration(TARGET_FETCH_RECURSIVELY, null,
364: FEATURES_RECURSIVELY, null, null);
365:
366: IIncludedFeatureReference[] compiledFeatures = ((Feature) feature)
367: .getFeatureIncluded();
368: for (int i = 0; i < compiledFeatures.length; i++) {
369: String featureId = compiledFeatures[i]
370: .getVersionedIdentifier().getIdentifier();
371: if (featureProperties
372: .containsKey(GENERATION_SOURCE_FEATURE_PREFIX
373: + featureId)) {
374: String[] extraElementsToFetch = Utils
375: .getArrayFromString(
376: featureProperties
377: .getProperty(GENERATION_SOURCE_FEATURE_PREFIX
378: + featureId), ","); //$NON-NLS-1$
379: for (int j = 1; j < extraElementsToFetch.length; j++) {
380: Object[] infos = Utils.parseExtraBundlesString(
381: extraElementsToFetch[j], false);
382: generateFetchEntry((String) infos[0],
383: (Version) infos[1], false);
384: }
385: continue;
386: }
387:
388: //Included features can be available in the baseLocation.
389: if (getRepositoryInfo(IFetchFactory.ELEMENT_TYPE_FEATURE
390: + '@' + featureId, new Version(compiledFeatures[i]
391: .getVersionedIdentifier().getVersion().toString())) != null)
392: script
393: .printAntTask(
394: Utils
395: .getPropertyFormat(PROPERTY_BUILD_DIRECTORY)
396: + '/'
397: + FETCH_FILE_PREFIX
398: + featureId + ".xml", null, TARGET_FETCH, null, null, null); //$NON-NLS-1$
399: else if (getSite(false).findFeature(featureId, null, false) == null) {
400: String message = NLS.bind(
401: Messages.error_cannotFetchNorFindFeature,
402: featureId);
403: throw new CoreException(new Status(IStatus.ERROR,
404: PI_PDEBUILD, EXCEPTION_FEATURE_MISSING,
405: message, null));
406: }
407: }
408: script.printTargetEnd();
409: }
410:
411: protected boolean generateFetchEntry(String entry, Version version,
412: boolean manifestFileOnly) throws CoreException {
413: Map mapFileEntry = mapInfos;
414: if (!entry.equals(element)) {
415: mapFileEntry = processMapFileEntry(entry, version);
416: if (mapFileEntry == null)
417: return false;
418: }
419:
420: IFetchFactory factory = (IFetchFactory) mapFileEntry
421: .get(FETCH_TASK_FACTORY);
422: String elementToFetch = (String) mapFileEntry
423: .get(IFetchFactory.KEY_ELEMENT_NAME);
424: String type = (String) mapFileEntry
425: .get(IFetchFactory.KEY_ELEMENT_TYPE);
426: if (!manifestFileOnly)
427: factory
428: .generateRetrieveElementCall(mapFileEntry,
429: computeFinalLocation(type, elementToFetch,
430: (Version) mapFileEntry
431: .get(MATCHED_VERSION)),
432: script);
433: else {
434: String[] files;
435: if (type.equals(IFetchFactory.ELEMENT_TYPE_FEATURE)) {
436: files = new String[] { Constants.FEATURE_FILENAME_DESCRIPTOR };
437: } else if (type.equals(IFetchFactory.ELEMENT_TYPE_PLUGIN)) {
438: files = new String[] {
439: Constants.PLUGIN_FILENAME_DESCRIPTOR,
440: Constants.BUNDLE_FILENAME_DESCRIPTOR };
441: } else if (type.equals(IFetchFactory.ELEMENT_TYPE_FRAGMENT)) {
442: files = new String[] {
443: Constants.FRAGMENT_FILENAME_DESCRIPTOR,
444: Constants.BUNDLE_FILENAME_DESCRIPTOR };
445: } else if (type.equals(IFetchFactory.ELEMENT_TYPE_BUNDLE)) {
446: files = new String[] { Constants.BUNDLE_FILENAME_DESCRIPTOR };
447: } else {
448: files = new String[0];
449: }
450: factory
451: .generateRetrieveFilesCall(mapFileEntry,
452: computeFinalLocation(type, elementToFetch,
453: (Version) mapFileEntry
454: .get(MATCHED_VERSION)),
455: files, script);
456: }
457:
458: //Keep track of the element that are being fetched. To simplify the lookup in the qualifier replacer, the versioned that was initially looked up is used as key in the file
459: Properties tags = null;
460: if (type.equals(IFetchFactory.ELEMENT_TYPE_FEATURE))
461: tags = repositoryFeatureTags;
462: else
463: tags = repositoryPluginTags;
464: if (mapFileEntry.get(IFetchFactory.KEY_ELEMENT_TAG) != null)
465: tags.put(elementToFetch
466: + ','
467: + new Version(version.getMajor(), version
468: .getMinor(), version.getMicro()),
469: mapFileEntry.get(IFetchFactory.KEY_ELEMENT_TAG));
470:
471: return true;
472: }
473:
474: /**
475: * Helper method to control for what locations a mkdir Ant task was already
476: * generated so we can reduce replication.
477: *
478: * @param location
479: */
480: protected void generateMkdirs(String location) {
481: if (mkdirLocations.contains(location))
482: return;
483: mkdirLocations.add(location);
484: script.printMkdirTask(location);
485: }
486:
487: /**
488: *
489: * @throws CoreException
490: */
491: protected void generateChildrenFetchScript() throws CoreException {
492: IPluginEntry[] allChildren = feature.getRawPluginEntries();
493: IPluginEntry[] compiledChildren = feature.getPluginEntries();
494:
495: for (int i = 0; i < allChildren.length; i++) {
496: String elementId = allChildren[i].getVersionedIdentifier()
497: .getIdentifier();
498: Version versionId = new Version(allChildren[i]
499: .getVersionedIdentifier().getVersion().toString());
500: // We are not fetching the elements that are said to be generated, but we are fetching some elements that can be associated
501: if (featureProperties
502: .containsKey(GENERATION_SOURCE_PLUGIN_PREFIX
503: + elementId)) {
504: String[] extraElementsToFetch = Utils
505: .getArrayFromString(
506: featureProperties
507: .getProperty(GENERATION_SOURCE_PLUGIN_PREFIX
508: + elementId), ","); //$NON-NLS-1$
509: for (int j = 1; j < extraElementsToFetch.length; j++) {
510: Object[] infos = Utils.parseExtraBundlesString(
511: extraElementsToFetch[j], false);
512: generateFetchEntry((String) infos[0],
513: (Version) infos[1], false);
514: }
515: continue;
516: }
517:
518: boolean generated = true;
519: if (allChildren[i].isFragment())
520: generated = generateFetchEntry(
521: IFetchFactory.ELEMENT_TYPE_FRAGMENT + '@'
522: + elementId, versionId, !Utils.isIn(
523: compiledChildren, allChildren[i]));
524: else
525: generated = generateFetchEntry(
526: IFetchFactory.ELEMENT_TYPE_PLUGIN + '@'
527: + elementId, versionId, !Utils.isIn(
528: compiledChildren, allChildren[i]));
529: if (generated == false)
530: generateFetchEntry(IFetchFactory.ELEMENT_TYPE_BUNDLE
531: + '@' + elementId, versionId, !Utils.isIn(
532: compiledChildren, allChildren[i]));
533: }
534: }
535:
536: /**
537: * Return the feature object for the feature with the given info. Generate an Ant script
538: * which will retrieve the "feature.xml" file from CVS, and then call the feature object
539: * constructor from Update.
540: *
541: * @param elementName the feature to retrieve
542: * @param elementType the element type
543: * @param elementInfos the element information
544: * @throws CoreException
545: */
546: protected void retrieveFeature(String elementName,
547: String elementType, Map elementInfos) throws CoreException {
548: // Generate a temporary Ant script which retrieves the feature.xml for this
549: // feature from CVS
550: File root = new File(workingDirectory);
551:
552: //generated container feature should already exist on disk
553: if (elementName.equals(IPDEBuildConstants.CONTAINER_FEATURE)) {
554: FeatureExecutableFactory factory = new FeatureExecutableFactory();
555: File featuresFolder = new File(root,
556: DEFAULT_FEATURE_LOCATION);
557: File featureLocation = new File(featuresFolder, elementName);
558: try {
559: feature = factory.createFeature(
560: featureLocation.toURL(), null, null);
561: featureProperties = new Properties();
562: InputStream featureStream = new BufferedInputStream(
563: new FileInputStream(new File(featureLocation,
564: PROPERTIES_FILE)));
565: featureProperties.load(featureStream);
566: featureStream.close();
567: return;
568: } catch (Exception e) {
569: String message = NLS.bind(
570: Messages.exception_missingFeature, elementName);
571: throw new CoreException(new Status(IStatus.ERROR,
572: PI_PDEBUILD, EXCEPTION_FEATURE_MISSING,
573: message, e));
574: }
575: }
576:
577: File target = new File(root,
578: DEFAULT_RETRIEVE_FILENAME_DESCRIPTOR);
579: IPath destination = new Path(root.getAbsolutePath())
580: .append("tempFeature/"); //$NON-NLS-1$
581: try {
582: AntScript retrieve = new AntScript(
583: new BufferedOutputStream(new FileOutputStream(
584: target)));
585: try {
586: retrieve.printProjectDeclaration(
587: "RetrieveFeature", "main", "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
588: retrieve.printTargetDeclaration(TARGET_MAIN, null,
589: null, null, null);
590:
591: String[] files = new String[] {
592: Constants.FEATURE_FILENAME_DESCRIPTOR,
593: PROPERTIES_FILE };
594: IFetchFactory factory = (IFetchFactory) elementInfos
595: .get(FETCH_TASK_FACTORY);
596: factory.generateRetrieveFilesCall(elementInfos,
597: destination, files, retrieve);
598:
599: retrieve.printTargetEnd();
600: retrieve.printProjectEnd();
601: } finally {
602: retrieve.close();
603: }
604: } catch (IOException e) {
605: String message = NLS.bind(Messages.exception_writeScript,
606: target);
607: throw new CoreException(new Status(IStatus.ERROR,
608: PI_PDEBUILD, EXCEPTION_WRITING_SCRIPT, message, e));
609: }
610:
611: // Run the Ant script to go to and retrieve the feature.xml. Call the Update
612: // code to construct the feature object to return.
613: try {
614: AntRunner runner = new AntRunner();
615: runner.setBuildFileLocation(target.getAbsolutePath());
616: Map retrieveProp = new HashMap();
617: retrieveProp.put("fetch.failonerror", "true"); //$NON-NLS-1$//$NON-NLS-2$
618: runner.addUserProperties(retrieveProp);
619: //This has to be hardcoded here because of the way AntRunner stipulates that
620: //loggers are passed in. Otherwise this would be a Foo.class.getName()
621: runner
622: .addBuildLogger("org.eclipse.pde.internal.build.tasks.SimpleBuildLogger"); //$NON-NLS-1$
623:
624: runner.run();
625: } catch (Exception e) {
626: throw new CoreException(new Status(IStatus.ERROR,
627: PI_PDEBUILD, EXCEPTION_FEATURE_MISSING,
628: NLS
629: .bind(Messages.error_retrieveFailed,
630: elementName), e));
631: }
632:
633: try {
634: FeatureExecutableFactory factory = new FeatureExecutableFactory();
635: File featureFolder = new File(destination.toString());
636: feature = factory.createFeature(featureFolder.toURL(),
637: null, null);
638:
639: //We only delete here, so if an exception is thrown the user can still see the retrieve.xml
640: target.delete();
641:
642: featureProperties = new Properties();
643: InputStream featureStream = new BufferedInputStream(
644: new FileInputStream(new File(featureFolder,
645: PROPERTIES_FILE)));
646: featureProperties.load(featureStream);
647: featureStream.close();
648: clear(featureFolder);
649: if (feature == null) {
650: String message = NLS.bind(
651: Messages.exception_missingFeature, elementName);
652: throw new CoreException(new Status(IStatus.ERROR,
653: PI_PDEBUILD, EXCEPTION_FEATURE_MISSING,
654: message, null));
655: }
656: } catch (Exception e) {
657: String message = NLS.bind(Messages.error_fetchingFeature,
658: elementName);
659: throw new CoreException(new Status(IStatus.ERROR,
660: PI_PDEBUILD, EXCEPTION_FEATURE_MISSING, message, e));
661: }
662: }
663:
664: /**
665: * Deletes all the files and directories from the given root down (inclusive).
666: * Returns false if we could not delete some file or an exception occurred
667: * at any point in the deletion.
668: * Even if an exception occurs, a best effort is made to continue deleting.
669: *
670: * @param root
671: * @return boolean
672: */
673: public static boolean clear(File root) {
674: boolean result = true;
675: if (root.isDirectory()) {
676: String[] list = root.list();
677: // for some unknown reason, list() can return null.
678: // Just skip the children If it does.
679: if (list != null)
680: for (int i = 0; i < list.length; i++)
681: result &= clear(new java.io.File(root, list[i]));
682: }
683: try {
684: if (root.exists())
685: result &= root.delete();
686: } catch (Exception e) {
687: // ignore any exceptions
688: result = false;
689: }
690: return result;
691: }
692:
693: protected IPath computeFinalLocation(String type,
694: String elementName, Version version) {
695: IPath location = new Path(Utils
696: .getPropertyFormat(PROPERTY_BUILD_DIRECTORY));
697: if (type.equals(IFetchFactory.ELEMENT_TYPE_FEATURE))
698: location = location.append(DEFAULT_FEATURE_LOCATION);
699: else
700: location = location.append(DEFAULT_PLUGIN_LOCATION);
701: return location
702: .append(elementName
703: + (version.equals(Version.emptyVersion) ? "" : '_' + version.toString())); //$NON-NLS-1$
704: }
705:
706: /**
707: * Get information stored in the directory file.
708: *
709: * @param elementName
710: * @return String
711: * @throws CoreException
712: */
713: //There are 3 cases described by the following "table"
714: // what is being asked --> what should be returned (what is in the map file)
715: // 1) id --> id (map: id, id@version)
716: // 2) id + version --> id@version (map: id@version, id@version2)
717: // 3) id --> highest version for the id (map: id@version1, id@version2)
718: // 4) id + version --> id (map: id)
719: // The first two cases are straight lookup cases
720: // The third case is a "fallback case"
721: // The fourth is the backward compatibility case.
722: protected Object[] getRepositoryInfo(String elementName,
723: Version version) throws CoreException {
724: //TODO Need to see if the element name contains plugin, bundle, etc...
725: if (directoryFile == null) {
726: directoryFile = readProperties(directoryLocation,
727: "", IStatus.ERROR); //$NON-NLS-1$
728: }
729:
730: String result = null;
731: Version matchedVersion = null;
732: //Here we deal with the simple cases: the looked up element exists as is in the map (cases 1 and 2).
733: if (Version.emptyVersion.equals(version)) {
734: result = (String) directoryFile.get(elementName);
735: matchedVersion = Version.emptyVersion;
736: } else {
737: result = (String) directoryFile.get(elementName + ','
738: + version.getMajor() + '.' + version.getMinor()
739: + '.' + version.getMicro());
740: matchedVersion = new Version(version.getMajor(), version
741: .getMinor(), version.getMicro());
742: if (result == null) {
743: result = (String) directoryFile.get(elementName); //case 4
744: matchedVersion = Version.emptyVersion;
745: if (result != null
746: && version
747: .getQualifier()
748: .endsWith(
749: IBuildPropertiesConstants.PROPERTY_QUALIFIER)) {
750: String message = NLS.bind(
751: Messages.warning_fallBackVersion,
752: elementName + ',' + version.toString(),
753: elementName);
754: BundleHelper.getDefault().getLog().log(
755: new Status(IStatus.WARNING, PI_PDEBUILD,
756: EXCEPTION_ENTRY_MISSING, message,
757: null));
758: }
759: }
760: }
761: if (result != null)
762: return new Object[] { result, matchedVersion };
763:
764: //Here we start dealing with the case #3.
765: initializeSortedDirectory();
766: //Among all the plug-ins, find all the ones for the given elementName
767: SortedMap candidates = directory.subMap(new MapFileEntry(
768: elementName, Version.emptyVersion), new MapFileEntry(
769: elementName, versionMax));
770: if (candidates.size() == 0)
771: return null;
772:
773: Map.Entry bestMatch = null;
774: for (Iterator iterator = candidates.entrySet().iterator(); iterator
775: .hasNext();) {
776: Map.Entry entry = (Map.Entry) iterator.next();
777: MapFileEntry aCandidate = (MapFileEntry) entry.getKey();
778: //Find the exact match
779: if (aCandidate.v.equals(version))
780: return new String[] { (String) entry.getValue(),
781: version.toString() };
782:
783: if (bestMatch != null) {
784: if (((MapFileEntry) bestMatch.getKey()).v
785: .compareTo(((MapFileEntry) entry.getKey()).v) < 1) {
786: bestMatch = entry;
787: }
788: } else {
789: bestMatch = entry;
790: }
791: }
792: if (!Version.emptyVersion.equals(version)) //The request was for a particular version number and it has not been found
793: return null;
794: return new Object[] { (String) bestMatch.getValue(),
795: ((MapFileEntry) bestMatch.getKey()).v };
796: }
797:
798: private static final Version versionMax = new Version(
799: Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
800:
801: private void initializeSortedDirectory() {
802: if (directory != null)
803: return;
804: directory = new TreeMap();
805: for (Iterator iter = directoryFile.entrySet().iterator(); iter
806: .hasNext();) {
807: Map.Entry entry = (Map.Entry) iter.next();
808: String[] entryInfo = Utils
809: .getArrayFromString((String) entry.getKey());
810: if (entryInfo.length == 0)
811: continue;
812: directory.put(new MapFileEntry(entryInfo[0],
813: entryInfo.length == 2 ? new Version(entryInfo[1])
814: : Version.emptyVersion), entry.getValue());
815: }
816: }
817:
818: public class MapFileEntry implements Comparable {
819: String id;
820: Version v;
821:
822: public MapFileEntry(String id, Version v) {
823: this .id = id;
824: this .v = v;
825: }
826:
827: public int compareTo(Object o) {
828: if (o instanceof MapFileEntry) {
829: MapFileEntry entry = (MapFileEntry) o;
830: int result = id.compareTo(entry.id);
831: if (result != 0)
832: return result;
833: return v.compareTo(entry.v);
834: }
835: return -1;
836: }
837:
838: public boolean equals(Object o) {
839: if (o instanceof MapFileEntry) {
840: MapFileEntry entry = (MapFileEntry) o;
841: return id.equals(entry.id) && v.equals(entry.v);
842: }
843: return false;
844: }
845: }
846:
847: /**
848: * Defines, the XML declaration and Ant project.
849: */
850: protected void generatePrologue() {
851: script.println();
852: script.printComment("Fetch script for " + element); //$NON-NLS-1$
853: script.println();
854: script.printProjectDeclaration(
855: "FetchScript", TARGET_FETCH, null); //$NON-NLS-1$
856: script.printProperty(PROPERTY_QUIET, "true"); //$NON-NLS-1$
857: }
858:
859: /**
860: * Just ends the script.
861: */
862: protected void generateEpilogue() {
863: script.println();
864: script.printProjectEnd();
865: }
866:
867: /**
868: * Generates additional targets submitted by the fetch task factory.
869: */
870: private void generateAdditionalTargets() {
871: for (Iterator iter = encounteredTypeOfRepo.iterator(); iter
872: .hasNext();) {
873: ((IFetchFactory) iter.next()).addTargets(script);
874: }
875: }
876:
877: /**
878: * Set the directory location to be the given value.
879: *
880: * @param directoryLocation
881: */
882: public void setDirectoryLocation(String directoryLocation) {
883: this .directoryLocation = directoryLocation;
884: }
885:
886: /**
887: * Sets whether children of the current element should be fetched.
888: *
889: * @param fetchChildren
890: */
891: public void setFetchChildren(boolean fetchChildren) {
892: this .fetchChildren = fetchChildren;
893: }
894:
895: /**
896: * Sets the CVS tag to use when fetching. This overrides whatever is
897: * in the directory database. This is typically used when doing a nightly
898: * build by setting the tag to HEAD.
899: *
900: * @param value a string CVS tag
901: */
902: public void setFetchTag(Properties value) {
903: fetchTags = value;
904: }
905:
906: /**
907: * Sets the CVS tag to use when fetching. This overrides whatever is
908: * in the directory database. This is typically used when doing a nightly
909: * build by setting the tag to HEAD.
910: *
911: * @param value a string CVS tag
912: */
913: public void setFetchTagAsString(String value) {
914: fetchTags = new Properties();
915: String[] entries = Utils.getArrayFromString(value);
916: //Backward compatibility
917: if (entries.length == 1 && (entries[0].indexOf('=') == -1)) {
918: fetchTags.put(CVSFetchTaskFactory.OVERRIDE_TAG, entries[0]);
919: return;
920: }
921: for (int i = 0; i < entries.length; i++) {
922: String[] valueForRepo = Utils.getArrayFromString(
923: entries[i], "="); //$NON-NLS-1$
924: if (valueForRepo == null || valueForRepo.length != 2)
925: throw new IllegalArgumentException(
926: "FetchTag " + entries[i]); //$NON-NLS-1$
927: fetchTags.put(valueForRepo[0], valueForRepo[1]);
928: }
929: }
930:
931: /**
932: * Sets the CVS password file location to be the given value.
933: *
934: * @param cvsPassFileLocation the CVS password file location
935: */
936: public void setCvsPassFileLocation(String cvsPassFileLocation) {
937: this .cvsPassFileLocation = cvsPassFileLocation;
938: }
939:
940: public void setRecursiveGeneration(boolean recursiveGeneration) {
941: this .recursiveGeneration = recursiveGeneration;
942: }
943:
944: private void setDirectory(SortedMap dir) {
945: directory = dir;
946: }
947:
948: private void setDirectoryFile(Properties dir) {
949: directoryFile = dir;
950: }
951: }
|