/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.branch; import static org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants.ATT_CATEGORY_ID; import static org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants.ATT_DESCRIPTION; import static org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants.ATT_ICON; import static org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants.ATT_ID; import static org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants.ATT_NAME; import static org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants.TAG_ENABLEMENT; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.expressions.EvaluationResult; import org.eclipse.core.expressions.Expression; import org.eclipse.core.expressions.ExpressionConverter; import org.eclipse.core.expressions.IEvaluationContext; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.xmind.gef.graphicalpolicy.IStructure; import org.xmind.gef.graphicalpolicy.IStyleSelector; import org.xmind.gef.part.IPart; import org.xmind.ui.branch.IBranchHook; import org.xmind.ui.branch.IBranchPolicyAdvisor; import org.xmind.ui.branch.IBranchPolicyDescriptor; import org.xmind.ui.branch.IBranchPropertyTester; import org.xmind.ui.internal.RegistryConstants; import org.xmind.ui.mindmap.IBranchPart; import org.xmind.ui.mindmap.ICacheManager; import org.xmind.ui.style.Styles; import org.xmind.ui.util.Logger; import org.xmind.ui.util.MindMapUtils; class BranchPolicy extends AbstractBranchPolicy implements IBranchPolicyDescriptor { private static final String SHOULD_NOTIFY_POST_DEACTIVATE = "org.xmind.ui.branch.internal.shouldNotifyPostDeactivate"; //$NON-NLS-1$ private IConfigurationElement element; private ImageDescriptor icon; private Expression enablementCondition; private Expression overrideCondition; private String defaultStructureId; private IStructureDescriptor defaultStructure; private List<AdditionalStructure> additionalStructures; private IBranchPropertyTester propertyTester; private boolean triedLoadingPropertyTester; private BranchHookFactory branchHookFactory; private boolean triedLoadingBranchHook; private Set<String> structureCacheKeys; private Map<String, Map<String, List<UnmodifiableProperty>>> unmodifiableProperties; private IBranchPolicyAdvisor advisor; private boolean triedLoadingAdvisor; private Expression advisorCondition; public BranchPolicy(BranchPolicyManager manager, IConfigurationElement element) throws CoreException { super(manager, element.getAttribute(ATT_ID)); this.element = element; load(); } private void load() throws CoreException { defaultStructureId = element .getAttribute(RegistryConstants.ATT_DEFAULT_STRUCTURE_ID); if (defaultStructureId == null) throw new CoreException(new Status(IStatus.ERROR, element.getNamespaceIdentifier(), 0, "Invalid extension (missing default structure id): " //$NON-NLS-1$ + getId(), null)); initializeEnablement(); initializeOverride(); } private void initializeEnablement() { IConfigurationElement[] elements = element .getChildren(IWorkbenchRegistryConstants.TAG_ENABLEMENT); if (elements.length == 0) return; try { enablementCondition = ExpressionConverter.getDefault().perform( elements[0]); } catch (CoreException e) { Logger.log(e, "Unable to convert enablement expression:" + getId()); //$NON-NLS-1$ } } private void initializeOverride() { IConfigurationElement[] overrideElements = element .getChildren(RegistryConstants.TAG_OVERRIDE); if (overrideElements.length == 0) return; IConfigurationElement[] elements = overrideElements[0] .getChildren(IWorkbenchRegistryConstants.TAG_ENABLEMENT); if (elements.length == 0) return; try { overrideCondition = ExpressionConverter.getDefault().perform( elements[0]); } catch (CoreException e) { Logger.log(e, "Unable to convert override expression:" + getId()); //$NON-NLS-1$ } } public String getId() { return getPolicyId(); } public ImageDescriptor getIcon() { if (icon == null) { icon = createIcon(); } return icon; } private ImageDescriptor createIcon() { String iconName = element.getAttribute(ATT_ICON); if (iconName != null) { String plugId = element.getNamespaceIdentifier(); return AbstractUIPlugin.imageDescriptorFromPlugin(plugId, iconName); } return null; } public String getName() { return element.getAttribute(ATT_NAME); } public String getDescription() { return element.getAttribute(ATT_DESCRIPTION); } public String getCategoryId() { return element.getAttribute(ATT_CATEGORY_ID); } public String getName(IBranchPart branch) { return getName(); } public ImageDescriptor getIcon(IBranchPart branch) { return getIcon(); } public boolean isApplicableTo(IBranchPart branch) { if (enablementCondition == null) return true; return isApplicableTo(BranchPolicyManager .createBranchEvaluationContext(branch)); } boolean isApplicableTo(IEvaluationContext context) { if (enablementCondition == null) return true; try { EvaluationResult result = enablementCondition.evaluate(context); return result == EvaluationResult.TRUE; } catch (CoreException e) { Logger.log(e, "Evaluation failed on: " + getId()); //$NON-NLS-1$ } return false; } boolean canOverride(IEvaluationContext context) { if (overrideCondition == null) return false; try { EvaluationResult result = overrideCondition.evaluate(context); return result == EvaluationResult.TRUE; } catch (CoreException e) { Logger.log(e, "Evaluation failed on: " + getId()); //$NON-NLS-1$ } return false; } private IStructureDescriptor getDefaultStructure() { if (defaultStructure == null) { defaultStructure = manager .getStructureDescriptor(defaultStructureId); } return defaultStructure; } public String getDefaultStructureId() { return defaultStructureId; } private List<AdditionalStructure> getAdditionalStructures() { if (additionalStructures == null) { loadAdditionalStructures(); if (additionalStructures == null) { additionalStructures = Collections.emptyList(); } } return additionalStructures; } private void loadAdditionalStructures() { IConfigurationElement[] children = element .getChildren(RegistryConstants.TAG_ADDITIONAL_STRUCTURES); if (children.length == 0) return; children = children[0] .getChildren(RegistryConstants.TAG_ADDITIONAL_STRUCTURE); for (IConfigurationElement child : children) { readAdditionalStructure(child); } } private void readAdditionalStructure(IConfigurationElement element) { try { AdditionalStructure additionalStructure = new AdditionalStructure( manager, element); registerAdditionStructure(additionalStructure); } catch (CoreException e) { Logger.log(e, "Failed to load additional structure: " //$NON-NLS-1$ + element); } } private void registerAdditionStructure( AdditionalStructure additionalStructure) { if (additionalStructures == null) additionalStructures = new ArrayList<AdditionalStructure>(); additionalStructures.add(additionalStructure); } private BranchHookFactory getBranchHookFactory() { if (branchHookFactory == null && !triedLoadingBranchHook) { loadBranchHookFactories(); triedLoadingBranchHook = true; } return branchHookFactory; } private void loadBranchHookFactories() { IConfigurationElement[] children = element .getChildren(RegistryConstants.TAG_BRANCH_HOOK); if (children.length == 0) return; readBranchHookFactory(children[0]); } private void readBranchHookFactory(IConfigurationElement element) { try { branchHookFactory = new BranchHookFactory(element); } catch (CoreException e) { Logger.log(e, "Failed to load branch hook: " + element); //$NON-NLS-1$ } } protected void activateBranch(IBranchPart branch) { super.activateBranch(branch); ICacheManager cm = MindMapUtils.getCacheManager(branch); if (cm != null) { cm.setValueProvider(IBranchPropertyTester.CACHE_PROPERTY_TESTER, this); cm.flush(IBranchPropertyTester.CACHE_PROPERTY_TESTER); } if (shouldNotifyAdvisor(branch)) { IBranchPolicyAdvisor advisor = getAdvisor(); if (advisor != null) { advisor.postActivate(branch, this); } } } protected void deactivateBranch(IBranchPart branch) { boolean shouldNotifyAdvisor = shouldNotifyAdvisor(branch); // if (shouldNotifyAdvisor) { // IBranchPolicyAdvisor advisor = getAdvisor(); // if (advisor != null) { // advisor.deactivate(branch, this); // } // } ICacheManager cm = MindMapUtils.getCacheManager(branch); if (cm != null) { cm.flush(IBranchPropertyTester.CACHE_PROPERTY_TESTER); cm.removeValueProvider(IBranchPropertyTester.CACHE_PROPERTY_TESTER); } super.deactivateBranch(branch); if (cm != null) { cm.setCache(SHOULD_NOTIFY_POST_DEACTIVATE, Boolean.valueOf(shouldNotifyAdvisor)); } } private boolean shouldNotifyAdvisor(IBranchPart branch) { if (getAdvisor() != null) { if (advisorCondition != null) { IEvaluationContext context = BranchPolicyManager .createBranchEvaluationContext(branch); try { return advisorCondition.evaluate(context) == EvaluationResult.TRUE; } catch (CoreException e) { return false; } } return true; } return false; } protected IBranchHook createHook(IBranchPart branch) { BranchHookFactory factory = getBranchHookFactory(); if (factory != null) return factory.createHook(branch); return null; } public void postDeactivate(IBranchPart branch) { super.postDeactivate(branch); Object shouldUsePostDeactivator = MindMapUtils.getCache(branch, SHOULD_NOTIFY_POST_DEACTIVATE); if (shouldUsePostDeactivator instanceof Boolean && ((Boolean) shouldUsePostDeactivator).booleanValue()) { IBranchPolicyAdvisor deactivator = getAdvisor(); if (deactivator != null) { deactivator.postDeactivate(branch, this); } } MindMapUtils.flushCache(branch, SHOULD_NOTIFY_POST_DEACTIVATE); } protected void flushStructureCache(IBranchPart branch) { super.flushStructureCache(branch); for (String cacheKey : getStructureCacheKeys()) { MindMapUtils.flushCache(branch, cacheKey); } } private Set<String> getStructureCacheKeys() { if (structureCacheKeys == null) { IConfigurationElement[] children = element .getChildren(RegistryConstants.TAG_STRUCTURE_CACHES); if (children.length > 0) { children = children[0] .getChildren(RegistryConstants.TAG_STRUCTURE_CACHE); if (children.length > 0) { for (IConfigurationElement child : children) { readStructureCache(child); } } } if (structureCacheKeys == null) structureCacheKeys = Collections.emptySet(); } return structureCacheKeys; } private void readStructureCache(IConfigurationElement element) { String key = element.getAttribute(IWorkbenchRegistryConstants.ATT_KEY); if (key != null) { if (structureCacheKeys == null) structureCacheKeys = new HashSet<String>(); structureCacheKeys.add(key); } } private IBranchPropertyTester getPropertyTester() { if (propertyTester == null && !triedLoadingPropertyTester) { String propertyTesterId = element .getAttribute(RegistryConstants.ATT_PROPERTY_TESTER_ID); if (propertyTesterId != null) { propertyTester = manager.getPropertyTester(propertyTesterId); } triedLoadingPropertyTester = true; } return propertyTester; } protected IStructure createDefaultStructureAlgorithm() { return getDefaultStructure().getAlgorithm(); } protected IStyleSelector createDefaultStyleSelector() { IConfigurationElement[] children = element .getChildren(RegistryConstants.TAG_STYLE_SELECTOR); if (children.length > 0) { return new ContributedBranchStyleSelector(manager, children[0]); } return super.createDefaultStyleSelector(); } protected boolean isUnmodifiableProperty(IBranchPart branch, String primaryKey, String secondaryKey) { Map<String, Map<String, List<UnmodifiableProperty>>> unmoProps = getUnmodifiableProperties(); if (!unmoProps.isEmpty()) { Map<String, List<UnmodifiableProperty>> map = unmoProps .get(primaryKey); if (map != null) { if (secondaryKey == null) secondaryKey = Styles.NULL; List<UnmodifiableProperty> list = map.get(secondaryKey); if (list != null) { IEvaluationContext context = BranchPolicyManager .createBranchEvaluationContext(branch); for (UnmodifiableProperty unmoProp : list) { if (unmoProp.isApplicableTo(context)) { return true; } } } } } return false; } public Map<String, Map<String, List<UnmodifiableProperty>>> getUnmodifiableProperties() { if (unmodifiableProperties == null) { readUnmodifiableProperties(); if (unmodifiableProperties == null) unmodifiableProperties = Collections.emptyMap(); } return unmodifiableProperties; } private void readUnmodifiableProperties() { IConfigurationElement[] children = element .getChildren(RegistryConstants.TAG_UNMODIFIABLE_PROPERTIES); if (children.length == 0) return; children = children[0] .getChildren(RegistryConstants.TAG_UNMODIFIABLE_PROPERTY); for (IConfigurationElement child : children) { loadUnmodifiableProperty(child); } } private void loadUnmodifiableProperty(IConfigurationElement element) { try { UnmodifiableProperty unmodifiableProperty = new UnmodifiableProperty( element); registerUnmodifiableProperty(unmodifiableProperty); } catch (CoreException e) { Logger.log(e, "Failed to load unmodifiable property: " //$NON-NLS-1$ + element); } } private void registerUnmodifiableProperty( UnmodifiableProperty unmodifiableProperty) { String primaryKey = unmodifiableProperty.getPrimaryKey(); String secondaryKey = unmodifiableProperty.getSecondaryKey(); if (secondaryKey == null) { secondaryKey = Styles.NULL; } if (unmodifiableProperties == null) unmodifiableProperties = new HashMap<String, Map<String, List<UnmodifiableProperty>>>(); Map<String, List<UnmodifiableProperty>> map = unmodifiableProperties .get(primaryKey); if (map == null) { map = new HashMap<String, List<UnmodifiableProperty>>(); unmodifiableProperties.put(primaryKey, map); } List<UnmodifiableProperty> list = map.get(secondaryKey); if (list == null) { list = new ArrayList<UnmodifiableProperty>(); map.put(secondaryKey, list); } list.add(unmodifiableProperty); } public Object getValue(IPart part, String key) { if (IBranchPropertyTester.CACHE_PROPERTY_TESTER.equals(key)) { return getPropertyTester(); } return super.getValue(part, key); } protected String calcAdditionalStructureId(IBranchPart branch, IBranchPart parent) { List<AdditionalStructure> additionalStructures = getAdditionalStructures(); if (!additionalStructures.isEmpty()) { IEvaluationContext context = BranchPolicyManager .createBranchEvaluationContext(branch); for (AdditionalStructure additionalStructure : additionalStructures) { if (additionalStructure.isApplicableTo(context)) { return additionalStructure.getStructureId(); } } } return null; } private IBranchPolicyAdvisor getAdvisor() { if (advisor == null && !triedLoadingAdvisor) { triedLoadingAdvisor = true; IConfigurationElement[] children = element .getChildren(RegistryConstants.TAG_ADVISOR); if (children.length > 0) { IConfigurationElement advisorElement = children[0]; String att = advisorElement .getAttribute(IWorkbenchRegistryConstants.ATT_CLASS); if (att != null) { try { advisor = (IBranchPolicyAdvisor) advisorElement .createExecutableExtension(RegistryConstants.ATT_CLASS); loadAdvisorCondition(advisorElement); } catch (CoreException e) { Logger.log(e, "Failed to create Post Deactivator: " //$NON-NLS-1$ + getId()); } } } } return advisor; } private void loadAdvisorCondition(IConfigurationElement pdElement) { IConfigurationElement[] enablements = pdElement .getChildren(TAG_ENABLEMENT); if (enablements.length == 0) return; try { advisorCondition = ExpressionConverter.getDefault().perform( enablements[0]); } catch (CoreException e) { Logger.log(e, "Failed to convert Post Deactivator expression: " //$NON-NLS-1$ + getId()); } } public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof BranchPolicy)) return false; BranchPolicy that = (BranchPolicy) obj; return this.element == that.element; } }