/*******************************************************************************
* 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);
}
}