001: /*******************************************************************************
002: * Copyright (c) 2005, 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 Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.wizards.tools;
011:
012: import java.io.File;
013: import java.util.ArrayList;
014: import java.util.HashSet;
015: import java.util.Set;
016: import java.util.regex.Pattern;
017:
018: import org.eclipse.core.resources.IFile;
019: import org.eclipse.core.resources.IProject;
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.IPath;
022: import org.eclipse.core.runtime.IProgressMonitor;
023: import org.eclipse.core.runtime.Path;
024: import org.eclipse.jdt.core.IJavaElement;
025: import org.eclipse.jdt.core.IPackageFragment;
026: import org.eclipse.jdt.core.IPackageFragmentRoot;
027: import org.eclipse.jdt.core.JavaModelException;
028: import org.eclipse.jface.text.IDocument;
029: import org.eclipse.ltk.core.refactoring.TextFileChange;
030: import org.eclipse.osgi.service.resolver.BundleDescription;
031: import org.eclipse.osgi.service.resolver.ExportPackageDescription;
032: import org.eclipse.osgi.service.resolver.HostSpecification;
033: import org.eclipse.osgi.service.resolver.State;
034: import org.eclipse.pde.core.IBaseModel;
035: import org.eclipse.pde.core.build.IBuild;
036: import org.eclipse.pde.core.build.IBuildEntry;
037: import org.eclipse.pde.core.build.IBuildModel;
038: import org.eclipse.pde.core.plugin.IFragmentModel;
039: import org.eclipse.pde.core.plugin.IPluginAttribute;
040: import org.eclipse.pde.core.plugin.IPluginElement;
041: import org.eclipse.pde.core.plugin.IPluginExtension;
042: import org.eclipse.pde.core.plugin.IPluginExtensionPoint;
043: import org.eclipse.pde.core.plugin.IPluginModelBase;
044: import org.eclipse.pde.core.plugin.IPluginObject;
045: import org.eclipse.pde.core.plugin.IPluginParent;
046: import org.eclipse.pde.core.plugin.PluginRegistry;
047: import org.eclipse.pde.internal.core.ICoreConstants;
048: import org.eclipse.pde.internal.core.PDECore;
049: import org.eclipse.pde.internal.core.PDEManager;
050: import org.eclipse.pde.internal.core.TargetPlatformHelper;
051: import org.eclipse.pde.internal.core.ibundle.IBundle;
052: import org.eclipse.pde.internal.core.ibundle.IBundleModel;
053: import org.eclipse.pde.internal.core.ibundle.IManifestHeader;
054: import org.eclipse.pde.internal.core.ischema.IMetaAttribute;
055: import org.eclipse.pde.internal.core.ischema.ISchema;
056: import org.eclipse.pde.internal.core.ischema.ISchemaAttribute;
057: import org.eclipse.pde.internal.core.ischema.ISchemaElement;
058: import org.eclipse.pde.internal.core.schema.SchemaRegistry;
059: import org.eclipse.pde.internal.core.text.IDocumentAttributeNode;
060: import org.eclipse.pde.internal.core.text.IDocumentElementNode;
061: import org.eclipse.pde.internal.core.text.IDocumentTextNode;
062: import org.eclipse.pde.internal.core.text.IModelTextChangeListener;
063: import org.eclipse.pde.internal.core.text.bundle.Bundle;
064: import org.eclipse.pde.internal.core.text.bundle.BundleModel;
065: import org.eclipse.pde.internal.core.text.bundle.ExportPackageHeader;
066: import org.eclipse.pde.internal.core.text.bundle.ExportPackageObject;
067: import org.eclipse.pde.internal.core.text.bundle.ImportPackageHeader;
068: import org.eclipse.pde.internal.core.text.bundle.ImportPackageObject;
069: import org.eclipse.pde.internal.core.text.bundle.RequireBundleHeader;
070: import org.eclipse.pde.internal.core.text.bundle.RequireBundleObject;
071: import org.eclipse.pde.internal.core.text.bundle.SingleManifestHeader;
072: import org.eclipse.pde.internal.core.text.plugin.FragmentModel;
073: import org.eclipse.pde.internal.core.text.plugin.PluginModel;
074: import org.eclipse.pde.internal.core.util.CoreUtility;
075: import org.eclipse.pde.internal.core.util.ManifestUtils;
076: import org.eclipse.pde.internal.core.util.PatternConstructor;
077: import org.eclipse.pde.internal.ui.util.ModelModification;
078: import org.eclipse.pde.internal.ui.util.PDEModelUtility;
079: import org.eclipse.text.edits.MultiTextEdit;
080: import org.eclipse.text.edits.TextEdit;
081: import org.osgi.framework.Constants;
082:
083: public class OrganizeManifest implements IOrganizeManifestsSettings {
084:
085: private static String F_NL_PREFIX = "$nl$"; //$NON-NLS-1$
086: private static String[] F_ICON_EXTENSIONS = new String[] {
087: "BMP", "ICO", "JPEG", "JPG", "GIF", "PNG", "TIFF" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
088: };
089:
090: public static void organizeRequireBundles(IBundle bundle,
091: boolean removeImports) {
092: if (!(bundle instanceof Bundle))
093: return;
094:
095: RequireBundleHeader header = (RequireBundleHeader) ((Bundle) bundle)
096: .getManifestHeader(Constants.REQUIRE_BUNDLE);
097: if (header != null) {
098: RequireBundleObject[] bundles = header.getRequiredBundles();
099: for (int i = 0; i < bundles.length; i++) {
100: String pluginId = bundles[i].getId();
101: if (PluginRegistry.findModel(pluginId) == null) {
102: if (removeImports)
103: header.removeBundle(bundles[i]);
104: else {
105: bundles[i].setOptional(true);
106: }
107: }
108: }
109: }
110: }
111:
112: public static void organizeExportPackages(IBundle bundle,
113: IProject project, boolean addMissing,
114: boolean removeUnresolved) {
115: if (!addMissing && !removeUnresolved)
116: return;
117:
118: if (!(bundle instanceof Bundle))
119: return;
120:
121: ExportPackageHeader header = (ExportPackageHeader) bundle
122: .getManifestHeader(Constants.EXPORT_PACKAGE);
123: ExportPackageObject[] currentPkgs;
124: if (header == null) {
125: bundle.setHeader(Constants.EXPORT_PACKAGE, ""); //$NON-NLS-1$
126: header = (ExportPackageHeader) bundle
127: .getManifestHeader(Constants.EXPORT_PACKAGE);
128: currentPkgs = new ExportPackageObject[0];
129: } else
130: currentPkgs = header.getPackages();
131:
132: IManifestHeader bundleClasspathheader = bundle
133: .getManifestHeader(Constants.BUNDLE_CLASSPATH);
134:
135: IPackageFragmentRoot[] roots = ManifestUtils
136: .findPackageFragmentRoots(bundleClasspathheader,
137: project);
138: // Running list of packages in the project
139: Set packages = new HashSet();
140: for (int i = 0; i < roots.length; i++) {
141: try {
142: if (ManifestUtils.isImmediateRoot(roots[i])) {
143: IJavaElement[] elements = roots[i].getChildren();
144: for (int j = 0; j < elements.length; j++)
145: if (elements[j] instanceof IPackageFragment) {
146: IPackageFragment fragment = (IPackageFragment) elements[j];
147: String name = fragment.getElementName();
148: if (name.length() == 0)
149: name = "."; //$NON-NLS-1$
150: if ((fragment.hasChildren() || fragment
151: .getNonJavaResources().length > 0)) {
152: if (addMissing
153: && !header.hasPackage(name))
154: header
155: .addPackage(new ExportPackageObject(
156: header,
157: fragment,
158: Constants.VERSION_ATTRIBUTE));
159: else
160: packages.add(name);
161: }
162: }
163: }
164: } catch (JavaModelException e) {
165: }
166: }
167:
168: // Remove packages that don't exist
169: if (removeUnresolved)
170: for (int i = 0; i < currentPkgs.length; i++)
171: if (!packages.contains(currentPkgs[i].getName()))
172: header.removePackage(currentPkgs[i]);
173: }
174:
175: public static void markPackagesInternal(IBundle bundle,
176: String packageFilter) {
177: if (packageFilter == null || bundle == null
178: || !(bundle instanceof Bundle))
179: return;
180:
181: ExportPackageHeader header = (ExportPackageHeader) bundle
182: .getManifestHeader(Constants.EXPORT_PACKAGE);
183: if (header == null)
184: return;
185:
186: ExportPackageObject[] currentPkgs = header.getPackages();
187: Pattern pat = PatternConstructor.createPattern(packageFilter,
188: false);
189: for (int i = 0; i < currentPkgs.length; i++) {
190: String values = currentPkgs[i].getValueComponents()[0];
191: if (!currentPkgs[i].isInternal()
192: && currentPkgs[i].getFriends().length == 0
193: && pat.matcher(values).matches()) {
194: currentPkgs[i].setInternal(true);
195: }
196: }
197: }
198:
199: public static void organizeImportPackages(IBundle bundle,
200: boolean removeImports) {
201: if (!(bundle instanceof Bundle))
202: return;
203: ImportPackageHeader header = (ImportPackageHeader) ((Bundle) bundle)
204: .getManifestHeader(Constants.IMPORT_PACKAGE);
205: if (header == null)
206: return;
207: ImportPackageObject[] importedPackages = header.getPackages();
208: Set availablePackages = getAvailableExportedPackages();
209: // get Preference
210: for (int i = 0; i < importedPackages.length; i++) {
211: String pkgName = importedPackages[i].getName();
212: if (!availablePackages.contains(pkgName)) {
213: if (removeImports)
214: header.removePackage(importedPackages[i]);
215: else {
216: importedPackages[i].setOptional(true);
217: }
218: }
219: }
220: }
221:
222: private static final Set getAvailableExportedPackages() {
223: State state = TargetPlatformHelper.getState();
224: ExportPackageDescription[] packages = state
225: .getExportedPackages();
226: Set set = new HashSet();
227: for (int i = 0; i < packages.length; i++) {
228: set.add(packages[i].getName());
229: }
230: return set;
231: }
232:
233: public static void removeUnneededLazyStart(IBundle bundle) {
234: if (!(bundle instanceof Bundle))
235: return;
236: if (bundle.getHeader(Constants.BUNDLE_ACTIVATOR) == null) {
237: String[] remove = new String[] {
238: ICoreConstants.ECLIPSE_LAZYSTART,
239: ICoreConstants.ECLIPSE_AUTOSTART };
240: for (int i = 0; i < remove.length; i++) {
241: IManifestHeader lazy = ((Bundle) bundle)
242: .getManifestHeader(remove[i]);
243: if (lazy instanceof SingleManifestHeader)
244: ((SingleManifestHeader) lazy)
245: .setMainComponent(null);
246: }
247: }
248:
249: }
250:
251: public static TextFileChange[] removeUnusedKeys(
252: final IProject project, final IBundle bundle,
253: final IPluginModelBase modelBase) {
254: String localization = bundle.getLocalization();
255: if (localization == null)
256: localization = "plugin"; //$NON-NLS-1$
257: IFile propertiesFile = project.getFile(localization
258: + ".properties"); //$NON-NLS-1$
259: if (!propertiesFile.exists())
260: return new TextFileChange[0];
261:
262: return PDEModelUtility.changesForModelModication(
263: new ModelModification(propertiesFile) {
264: protected void modifyModel(IBaseModel model,
265: IProgressMonitor monitor)
266: throws CoreException {
267: if (!(model instanceof IBuildModel))
268: return;
269:
270: IBuild build = ((IBuildModel) model).getBuild();
271: IBuildEntry[] entries = build.getBuildEntries();
272: ArrayList allKeys = new ArrayList(
273: entries.length);
274: for (int i = 0; i < entries.length; i++)
275: if (!allKeys.contains(entries[i].getName()))
276: allKeys.add(entries[i].getName());
277:
278: ArrayList usedkeys = new ArrayList();
279: findTranslatedStrings(project, modelBase,
280: bundle, usedkeys);
281:
282: for (int i = 0; i < usedkeys.size(); i++)
283: allKeys.remove(usedkeys.get(i));
284:
285: if (allKeys.size() == 0)
286: return;
287:
288: // scan properties file for keys referencing other keys
289: for (int i = 0; i < entries.length; i++) {
290: String[] tokens = entries[i].getTokens();
291: if (tokens == null || tokens.length == 0)
292: continue;
293: String entry = tokens[0];
294: for (int k = 1; k < tokens.length; k++)
295: entry += ',' + tokens[k];
296: if (entry.indexOf('%') == entry
297: .lastIndexOf('%'))
298: continue;
299:
300: // allKeys must NOT have any duplicates
301: for (int j = 0; j < allKeys.size(); j++) {
302: String akey = '%' + (String) allKeys
303: .get(j) + '%';
304: if (entry.indexOf(akey) != -1)
305: allKeys.remove(allKeys.get(j--));
306: if (allKeys.size() == 0)
307: return;
308: }
309: }
310:
311: for (int i = 0; i < allKeys.size(); i++) {
312: IBuildEntry entry = build
313: .getEntry((String) allKeys.get(i));
314: build.remove(entry);
315: }
316: }
317: }, null);
318: }
319:
320: private static void findTranslatedStrings(IProject project,
321: IPluginModelBase pluginModel, IBundle bundle, ArrayList list) {
322:
323: findTranslatedXMLStrings(pluginModel, list);
324: findTranslatedMFStrings(bundle, list);
325:
326: IPluginModelBase model = PluginRegistry.findModel(project);
327:
328: BundleDescription bundleDesc = model.getBundleDescription();
329: HostSpecification hostSpec = bundleDesc.getHost();
330: if (hostSpec != null) {
331: BundleDescription[] hosts = hostSpec.getHosts();
332: for (int i = 0; i < hosts.length; i++) {
333: IPluginModelBase hostModel = PluginRegistry
334: .findModel(hosts[i]);
335: if (hostModel != null) {
336: findTranslatedXMLStrings(getTextModel(hostModel,
337: false), list);
338: findTranslatedMFStrings(getTextBundle(hostModel),
339: list);
340: }
341: }
342: } else {
343: IFragmentModel[] fragmentModels = PDEManager
344: .findFragmentsFor(model);
345: for (int i = 0; i < fragmentModels.length; i++) {
346: findTranslatedXMLStrings(getTextModel(
347: fragmentModels[i], true), list);
348: findTranslatedMFStrings(
349: getTextBundle(fragmentModels[i]), list);
350: }
351: }
352: }
353:
354: private static IPluginModelBase getTextModel(
355: IPluginModelBase model, boolean fragment) {
356: if (model instanceof PluginModel
357: || model instanceof FragmentModel)
358: return model;
359:
360: if (model != null) {
361: if (!fileExists(model.getInstallLocation(),
362: fragment ? F_FRAGMENT_FILE : F_PLUGIN_FILE))
363: return null;
364: IDocument doc = CoreUtility.getTextDocument(new File(model
365: .getInstallLocation()), fragment ? F_FRAGMENT_FILE
366: : F_PLUGIN_FILE);
367: IPluginModelBase returnModel;
368: if (fragment)
369: returnModel = new FragmentModel(doc, false);
370: else
371: returnModel = new PluginModel(doc, false);
372: try {
373: returnModel.load();
374: } catch (CoreException e) {
375: }
376:
377: if (returnModel.isLoaded())
378: return returnModel;
379: }
380: return null;
381: }
382:
383: private static IBundle getTextBundle(IPluginModelBase model) {
384: if (model != null) {
385: if (!fileExists(model.getInstallLocation(), F_MANIFEST_FILE))
386: return null;
387: IDocument doc = CoreUtility.getTextDocument(new File(model
388: .getInstallLocation()), F_MANIFEST_FILE);
389: IBundleModel bundleModel = new BundleModel(doc, false);
390: try {
391: bundleModel.load();
392: } catch (CoreException e) {
393: }
394:
395: if (bundleModel.isLoaded())
396: return bundleModel.getBundle();
397: }
398: return null;
399: }
400:
401: private static void findTranslatedXMLStrings(
402: IPluginModelBase model, ArrayList list) {
403: if (model == null)
404: return;
405:
406: IPluginExtensionPoint[] points = model.getPluginBase()
407: .getExtensionPoints();
408: for (int i = 0; i < points.length; i++) {
409: String value = getTranslatedKey(points[i].getName());
410: if (value != null && !list.contains(value))
411: list.add(value);
412: }
413: IPluginExtension[] extensions = model.getPluginBase()
414: .getExtensions();
415: for (int i = 0; i < extensions.length; i++)
416: if (extensions[i] instanceof IDocumentElementNode)
417: inspectElementForTranslation(
418: (IDocumentElementNode) extensions[i], list);
419: }
420:
421: private static void inspectElementForTranslation(
422: IDocumentElementNode parent, ArrayList list) {
423: IDocumentTextNode text = parent.getTextNode();
424: String textValue = getTranslatedKey(text != null ? text
425: .getText() : null);
426: if (textValue != null && !list.contains(textValue))
427: list.add(textValue);
428:
429: IDocumentAttributeNode[] attributes = parent
430: .getNodeAttributes();
431: for (int j = 0; j < attributes.length; j++) {
432: String attrValue = getTranslatedKey(attributes[j]
433: .getAttributeValue());
434: if (attrValue != null && !list.contains(attrValue))
435: list.add(attrValue);
436: }
437:
438: if (!(parent instanceof IPluginParent))
439: return;
440:
441: IPluginObject[] children = ((IPluginParent) parent)
442: .getChildren();
443: for (int i = 0; i < children.length; i++)
444: if (children[i] instanceof IDocumentElementNode)
445: inspectElementForTranslation(
446: (IDocumentElementNode) children[i], list);
447: }
448:
449: private static void findTranslatedMFStrings(IBundle bundle,
450: ArrayList list) {
451: if (bundle == null)
452: return;
453: for (int i = 0; i < ICoreConstants.TRANSLATABLE_HEADERS.length; i++) {
454: String key = getTranslatedKey(bundle
455: .getHeader(ICoreConstants.TRANSLATABLE_HEADERS[i]));
456: if (key != null && !list.contains(key))
457: list.add(key);
458: }
459: }
460:
461: private static String getTranslatedKey(String value) {
462: if (value != null && value.length() > 1
463: && value.charAt(0) == '%' && value.charAt(1) != '%')
464: return value.substring(1);
465: return null;
466: }
467:
468: private static boolean fileExists(String container, String filename) {
469: return new File(container + filename).exists();
470: }
471:
472: /**
473: * Finds all resource paths ending with a valid icon file extension and creates
474: * a text edit operation in <code>multiEdit</code> for each one that is not prefixed by an
475: * $nl$ segment.
476: *
477: * @param model -
478: */
479: public static void prefixIconPaths(IPluginModelBase model) {
480: if (model == null)
481: return;
482:
483: SchemaRegistry registry = PDECore.getDefault()
484: .getSchemaRegistry();
485: IPluginExtension[] extensions = model.getPluginBase()
486: .getExtensions();
487: for (int i = 0; i < extensions.length; i++) {
488: ISchema schema = registry.getSchema(extensions[i]
489: .getPoint());
490: if (schema != null)
491: inspectElementsIconPaths(schema, extensions[i]);
492: }
493: }
494:
495: private static void inspectElementsIconPaths(ISchema schema,
496: IPluginParent parent) {
497: IPluginObject[] children = parent.getChildren();
498: for (int i = 0; i < children.length; i++) {
499: IPluginElement child = (IPluginElement) children[i];
500: ISchemaElement schemaElement = schema.findElement(child
501: .getName());
502: if (schemaElement != null) {
503: IPluginAttribute[] attributes = child.getAttributes();
504: for (int j = 0; j < attributes.length; j++) {
505: ISchemaAttribute attInfo = schemaElement
506: .getAttribute(attributes[j].getName());
507: if (attInfo != null
508: && attInfo.getKind() == IMetaAttribute.RESOURCE) {
509: String value = attributes[j].getValue();
510: if (value.startsWith(F_NL_PREFIX))
511: continue;
512: int fileExtIndex = value.lastIndexOf('.');
513: if (fileExtIndex == -1)
514: continue;
515: value = value.substring(fileExtIndex + 1);
516: for (int e = 0; e < F_ICON_EXTENSIONS.length; e++) {
517: if (value
518: .equalsIgnoreCase(F_ICON_EXTENSIONS[e])) {
519: IPath path = new Path(F_NL_PREFIX);
520: String newValue = attributes[j]
521: .getValue();
522: if (newValue.charAt(0) != IPath.SEPARATOR)
523: path = path.addTrailingSeparator();
524: newValue = path.toString() + newValue;
525: try {
526: child.setAttribute(attributes[j]
527: .getName(), newValue);
528: } catch (CoreException e1) {
529: }
530: break;
531: }
532: }
533: }
534: }
535: }
536: inspectElementsIconPaths(schema, child);
537: }
538: }
539:
540: protected static MultiTextEdit getTextEdit(
541: IModelTextChangeListener listener) {
542: if (listener == null)
543: return null;
544: TextEdit[] edits = listener.getTextOperations();
545: if (edits.length == 0)
546: return null;
547: MultiTextEdit multiEdit = new MultiTextEdit();
548: multiEdit.addChildren(edits);
549: return multiEdit;
550: }
551:
552: }
|