/******************************************************************************* * Copyright (c) 2009 SpringSource, a divison of VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SpringSource, a division of VMware, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.virgo.ide.ui.editors; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.pde.core.IBaseModel; import org.eclipse.pde.core.plugin.IPluginLibrary; import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.internal.core.text.IDocumentKey; import org.eclipse.pde.internal.core.text.IDocumentRange; import org.eclipse.pde.internal.core.text.IEditingModel; import org.eclipse.pde.internal.core.text.bundle.Bundle; import org.eclipse.pde.internal.core.text.bundle.BundleClasspathHeader; import org.eclipse.pde.internal.core.text.bundle.BundleModel; import org.eclipse.pde.internal.core.text.bundle.ExecutionEnvironment; import org.eclipse.pde.internal.core.text.bundle.ExportPackageHeader; import org.eclipse.pde.internal.core.text.bundle.ExportPackageObject; import org.eclipse.pde.internal.core.text.bundle.ImportPackageHeader; import org.eclipse.pde.internal.core.text.bundle.ImportPackageObject; import org.eclipse.pde.internal.core.text.bundle.ManifestHeader; import org.eclipse.pde.internal.core.text.bundle.PackageObject; import org.eclipse.pde.internal.core.text.bundle.RequireBundleHeader; import org.eclipse.pde.internal.core.text.bundle.RequireBundleObject; import org.eclipse.pde.internal.core.text.bundle.RequiredExecutionEnvironmentHeader; import org.eclipse.pde.internal.core.text.plugin.PluginModel; import org.eclipse.pde.internal.ui.PDELabelProvider; import org.eclipse.pde.internal.ui.PDEPlugin; import org.eclipse.pde.internal.ui.PDEPluginImages; import org.eclipse.pde.internal.ui.editor.IFoldingStructureProvider; import org.eclipse.pde.internal.ui.editor.PDEFormEditor; import org.eclipse.pde.internal.ui.editor.PDEFormEditorContributor; import org.eclipse.pde.internal.ui.editor.actions.HyperlinkAction; import org.eclipse.pde.internal.ui.editor.actions.PDEActionConstants; import org.eclipse.pde.internal.ui.editor.plugin.BundleSourcePage; import org.eclipse.pde.internal.ui.editor.plugin.ExtensionHyperLink; import org.eclipse.pde.internal.ui.editor.plugin.PluginFoldingStructureProvider; import org.eclipse.pde.internal.ui.editor.text.ChangeAwareSourceViewerConfiguration; import org.eclipse.pde.internal.ui.editor.text.IColorManager; import org.eclipse.pde.internal.ui.editor.text.ReconcilingStrategy; import org.eclipse.pde.internal.ui.elements.DefaultContentProvider; import org.eclipse.pde.internal.ui.util.SharedLabelProvider; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.texteditor.ContentAssistAction; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.virgo.ide.manifest.core.IHeaderConstants; import org.eclipse.virgo.ide.manifest.core.editor.model.ImportBundleHeader; import org.eclipse.virgo.ide.manifest.core.editor.model.ImportBundleObject; import org.eclipse.virgo.ide.manifest.core.editor.model.ImportLibraryHeader; import org.eclipse.virgo.ide.manifest.core.editor.model.ImportLibraryObject; import org.eclipse.virgo.ide.ui.ServerIdeUiPlugin; import org.eclipse.virgo.ide.ui.editors.text.BundleColorManager; import org.eclipse.virgo.ide.ui.editors.text.BundleManifestConfiguration; import org.eclipse.virgo.ide.ui.internal.actions.ManifestFormatAction; import org.springframework.util.ReflectionUtils; /** * @author Christian Dupuis * @author Steffen Pingel * @author Leo Dos Santos */ public class SpringBundleSourcePage extends BundleSourcePage { private final String ID_CONTEXT_MENU = "org.eclipse.virgo.ide.bundlemanifest.text.menu"; private IFoldingStructureProvider fFoldingStructureProvider; private final ChangeAwareSourceViewerConfiguration fConfiguration; public SpringBundleSourcePage(PDEFormEditor editor, String id, String title) { super(editor, id, title); fConfiguration = createSourceViewerConfiguration(BundleColorManager.getDefault()); if (fConfiguration != null) { setSourceViewerConfiguration(fConfiguration); } } protected ChangeAwareSourceViewerConfiguration createSourceViewerConfiguration(IColorManager colorManager) { return new BundleManifestConfiguration(colorManager, this); } @Override protected void setEditorContextMenuId(String contextMenuId) { super.setEditorContextMenuId(ID_CONTEXT_MENU); } @Override public void projectionEnabled() { IBaseModel model = getInputContext().getModel(); if (model instanceof IEditingModel) { fFoldingStructureProvider = getFoldingStructureProvider((IEditingModel) model); if (fFoldingStructureProvider != null) { fFoldingStructureProvider.initialize(); IReconciler rec = getSourceViewerConfiguration().getReconciler(getSourceViewer()); IReconcilingStrategy startegy = rec.getReconcilingStrategy(new String()); if (startegy instanceof ReconcilingStrategy) { ((ReconcilingStrategy) startegy).addParticipant(fFoldingStructureProvider); } } } } protected IFoldingStructureProvider getFoldingStructureProvider(IEditingModel model) { if (model instanceof PluginModel) { return new PluginFoldingStructureProvider(this, model); } if (model instanceof BundleModel) { return new SpringBundleFoldingStructureProvider(this, model); } // return super.getFoldingStructureProvider(model); return null; } @Override public void projectionDisabled() { fFoldingStructureProvider = null; } @Override protected boolean affectsTextPresentation(PropertyChangeEvent event) { if (fConfiguration == null) { return false; } return fConfiguration.affectsTextPresentation(event) || super.affectsTextPresentation(event); } @Override public void dispose() { if (fConfiguration != null) { fConfiguration.dispose(); } super.dispose(); } @Override protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { try { if (fConfiguration != null) { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) { fConfiguration.adaptToPreferenceChange(event); } } } finally { super.handlePreferenceStoreChanged(event); } } @Override protected void editorContextMenuAboutToShow(IMenuManager menu) { super.editorContextMenuAboutToShow(menu); PDEFormEditorContributor contributor = ((PDEFormEditor) getEditor()).getContributor(); if (contributor instanceof BundleManifestEditorContributor) { BundleManifestEditorContributor textContributor = (BundleManifestEditorContributor) contributor; HyperlinkAction action = textContributor.getHyperlinkAction(); if ((action != null) && action.isEnabled() && ((action.getHyperLink() instanceof ExtensionHyperLink) == false)) { // Another detector handles this the extension hyperlink case // org.eclipse.pde.internal.ui.editor.plugin. // ExtensionAttributePointDectector.java // Implemented at a higher level. As a result, need to disable // the action here to prevent duplicate entries in the context // menu menu.add(action); } ManifestFormatAction formatManifestAction = textContributor.getFormatAction(); if (formatManifestAction != null && formatManifestAction.isEnabled()) { // add format action after Outline. This is the same order as // the hyperlink actions menu.insertAfter(PDEActionConstants.COMMAND_ID_QUICK_OUTLINE, formatManifestAction); } } } @Override protected void createActions() { super.createActions(); PDEFormEditorContributor contributor = ((PDEFormEditor) getEditor()).getContributor(); if (contributor instanceof BundleManifestEditorContributor) { BundleManifestEditorContributor textContributor = (BundleManifestEditorContributor) contributor; setAction(PDEActionConstants.OPEN, textContributor.getHyperlinkAction()); setAction(PDEActionConstants.FORMAT, textContributor.getFormatAction()); if (textContributor.supportsContentAssist()) { createContentAssistAction(); } } } private void createContentAssistAction() { IAction contentAssist = new ContentAssistAction(getBundleForConstructedKeys(), "ContentAssistProposal.", this); //$NON-NLS-1$ contentAssist.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); setAction("ContentAssist", contentAssist); //$NON-NLS-1$ markAsStateDependentAction("ContentAssist", true); //$NON-NLS-1$ } @Override public ILabelProvider createOutlineLabelProvider() { return new SpringBundleLabelProvider(); } @Override public ITreeContentProvider createOutlineContentProvider() { return new SpringBundleOutlineContentProvider(); } @Override public IDocumentRange findRange() { try { java.lang.reflect.Field field = ReflectionUtils.findField(this.getClass(), "fSelection", Object.class); field.setAccessible(true); Object selection = field.get(this); if (selection instanceof ImportLibraryObject) { return getSpecificRange(((ImportLibraryObject) selection).getModel(), IHeaderConstants.IMPORT_LIBRARY, ((ImportLibraryObject) selection).getId()); } else if (selection instanceof ImportBundleObject) { return getSpecificRange(((ImportBundleObject) selection).getModel(), IHeaderConstants.IMPORT_BUNDLE, ((ImportBundleObject) selection).getId()); } else if (selection instanceof ImportPackageObject) { ImportPackageObject impObj = (ImportPackageObject) selection; String key = impObj.getHeader().getKey(); if (key != null && key.equalsIgnoreCase(IHeaderConstants.IMPORT_TEMPLATE)) { return getSpecificRange(((ImportPackageObject) selection).getModel(), IHeaderConstants.IMPORT_TEMPLATE, ((ImportPackageObject) selection).getValue()); } else if (key != null && key.equalsIgnoreCase(IHeaderConstants.EXCLUDED_IMPORTS)) { return getSpecificRange(((ImportPackageObject) selection).getModel(), IHeaderConstants.EXCLUDED_IMPORTS, ((ImportPackageObject) selection).getValue()); } else if (key != null && key.equalsIgnoreCase(IHeaderConstants.UNVERSIONED_IMPORTS)) { return getSpecificRange(((ImportPackageObject) selection).getModel(), IHeaderConstants.UNVERSIONED_IMPORTS, ((ImportPackageObject) selection).getValue()); } } else if (selection instanceof ExportPackageObject) { ExportPackageObject expObj = (ExportPackageObject) selection; String key = expObj.getHeader().getKey(); if (key != null && key.equalsIgnoreCase(IHeaderConstants.EXPORT_TEMPLATE)) { return getSpecificRange(((ExportPackageObject) selection).getModel(), IHeaderConstants.EXPORT_TEMPLATE, ((ExportPackageObject) selection).getValue()); } else if (key != null && key.equalsIgnoreCase(IHeaderConstants.EXCLUDED_EXPORTS)) { return getSpecificRange(((ExportPackageObject) selection).getModel(), IHeaderConstants.EXCLUDED_EXPORTS, ((ExportPackageObject) selection).getValue()); } } else { return super.findRange(); } } catch (Exception e) { } return super.findRange(); } private class SpringBundleOutlineContentProvider extends DefaultContentProvider implements ITreeContentProvider { public Object[] getChildren(Object parent) { // Need an identifying class for label provider if (parent instanceof ImportPackageHeader) { return ((ImportPackageHeader) parent).getPackages(); } else if (parent instanceof ExportPackageHeader) { return ((ExportPackageHeader) parent).getPackages(); } else if (parent instanceof RequiredExecutionEnvironmentHeader) { return ((RequiredExecutionEnvironmentHeader) parent).getEnvironments(); } else if (parent instanceof RequireBundleHeader) { return ((RequireBundleHeader) parent).getRequiredBundles(); } else if (parent instanceof BundleClasspathHeader) { return getPluginLibraries(); } else if (parent instanceof ImportBundleHeader) { return ((ImportBundleHeader) parent).getImportedBundles(); } else if (parent instanceof ImportLibraryHeader) { return ((ImportLibraryHeader) parent).getImportedLibraries(); } return new Object[0]; } private Object[] getPluginLibraries() { IPluginLibrary[] libraries = getBundleClasspathLibraries(); if ((libraries == null) || (libraries.length == 0)) { return new Object[0]; } return libraries; } public boolean hasChildren(Object parent) { return getChildren(parent).length > 0; } public Object getParent(Object child) { return null; } @SuppressWarnings("unchecked") public Object[] getElements(Object parent) { if (parent instanceof BundleModel) { BundleModel model = (BundleModel) parent; Map manifest = ((Bundle) model.getBundle()).getHeaders(); ArrayList keys = new ArrayList(); for (Iterator elements = manifest.keySet().iterator(); elements.hasNext();) { IDocumentKey key = (IDocumentKey) manifest.get(elements.next()); if (key.getOffset() > -1) { keys.add(key); } } return keys.toArray(); } return new Object[0]; } } private IPluginLibrary[] getBundleClasspathLibraries() { // The bundle classpath header has no model data members // Retrieve the plug-in library equivalents from the editor model FormEditor editor = getEditor(); if (editor instanceof PDEFormEditor) { PDEFormEditor formEditor = (PDEFormEditor) editor; IBaseModel baseModel = formEditor.getAggregateModel(); if (baseModel instanceof IPluginModelBase) { IPluginLibrary[] libraries = ((IPluginModelBase) baseModel).getPluginBase().getLibraries(); return libraries; } } return null; } private class SpringBundleLabelProvider extends LabelProvider { // TODO: MP: QO: LOW: Move to PDELabelProvider @Override public String getText(Object obj) { if (obj instanceof PackageObject) { return ((PackageObject) obj).getName(); } else if (obj instanceof ExecutionEnvironment) { return ((ExecutionEnvironment) obj).getName(); } else if (obj instanceof RequireBundleObject) { return getTextRequireBundle(((RequireBundleObject) obj)); } else if (obj instanceof ImportLibraryObject) { return ((ImportLibraryObject) obj).getId(); } else if (obj instanceof ImportBundleObject) { return ((ImportBundleObject) obj).getId(); } else if (obj instanceof ManifestHeader) { return ((ManifestHeader) obj).getName(); } return super.getText(obj); } private String getTextRequireBundle(RequireBundleObject bundle) { StringBuffer label = new StringBuffer(); // Append the ID label.append(bundle.getId()); // Get the version String version = bundle.getVersion(); // If there is no version, just return what we have if ((version == null) || (version.length() == 0)) { return label.toString(); } // Append a space label.append(' '); // If the first character does not have a range indicator, // add a default one. This can happen when there is only one // value specified for either min or max char firstChar = version.charAt(0); if ((firstChar != '(') && (firstChar != '[')) { label.append('('); } // Append the version label.append(version); // If the last character does not have a range indicator, // add a default one. This can happen when there is only one // value specified for either min or max char lastChar = version.charAt(version.length() - 1); if ((lastChar != ')') && (lastChar != ']')) { label.append(')'); } // Return what we have return label.toString(); } @Override public Image getImage(Object obj) { PDELabelProvider labelProvider = PDEPlugin.getDefault().getLabelProvider(); if (obj instanceof PackageObject) { return labelProvider.get(PDEPluginImages.DESC_PACKAGE_OBJ); } else if (obj instanceof ExecutionEnvironment) { return labelProvider.get(PDEPluginImages.DESC_JAVA_LIB_OBJ); } else if (obj instanceof RequireBundleObject) { int flags = SharedLabelProvider.F_EXTERNAL; if (((RequireBundleObject) obj).isReexported()) { flags = flags | SharedLabelProvider.F_EXPORT; } return labelProvider.get(PDEPluginImages.DESC_REQ_PLUGIN_OBJ, flags); } else if (obj instanceof ImportLibraryObject) { return labelProvider.get(PDEPluginImages.DESC_JAR_LIB_OBJ); } else if (obj instanceof ImportBundleObject) { return labelProvider.get(PDEPluginImages.DESC_BUNDLE_OBJ); } else if (obj instanceof ManifestHeader) { if (isSpringHeader(((ManifestHeader) obj).getKey())) { return labelProvider.get(ServerIdeUiPlugin.getImageDescriptor("full/view16/green_ball_obj.gif")); } return labelProvider.get(PDEPluginImages.DESC_BUILD_VAR_OBJ); } else if (obj instanceof IPluginLibrary) { return labelProvider.get(PDEPluginImages.DESC_JAVA_LIB_OBJ); } return null; } } private boolean isSpringHeader(String key) { ResourceBundle bundle = ResourceBundle.getBundle("org.eclipse.virgo.ide.ui.editors.text.headers"); List<String> headers = Collections.list(bundle.getKeys()); return headers.contains(key); } @SuppressWarnings("unchecked") @Override public Object getAdapter(Class adapter) { if (IHyperlinkDetector.class.equals(adapter)) { return new SpringBundleHyperlinkDetector(this); } return super.getAdapter(adapter); } }