/******************************************************************************* * Copyright (c) 2007, 2016 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.help.internal; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.eclipse.core.expressions.EvaluationResult; import org.eclipse.core.expressions.Expression; import org.eclipse.core.expressions.ExpressionConverter; import org.eclipse.core.expressions.ExpressionTagNames; import org.eclipse.core.expressions.IEvaluationContext; import org.eclipse.core.runtime.CoreException; import org.eclipse.help.IUAElement; import org.eclipse.help.internal.dynamic.FilterResolver; import org.eclipse.help.internal.entityresolver.LocalEntityResolver; import org.eclipse.help.internal.util.ProductPreferences; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /* * Base class for UA model elements. */ public class UAElement implements IUAElement { private static final String ELEMENT_FILTER = "filter"; //$NON-NLS-1$ private static final String ATTRIBUTE_FILTER = "filter"; //$NON-NLS-1$ private static final String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ private static final String ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$ private static DocumentBuilder builder; private static Document document; private Element element; private UAElement parent; protected List<UAElement> children; private Filter[] filters; private Expression enablementExpression; private IUAElement src; private class Filter { public Filter(String name, String value, boolean isNegated) { this.name = name; this.value = value; this.isNegated = isNegated; } String name; String value; boolean isNegated; } public UAElement(Element element) { this.element = element; } public UAElement(String name) { this.element = getDocument().createElement(name); } public UAElement(String name, IUAElement src) { this(name); if (src instanceof UAElement) { copyFilters(src); } else { this.src = src; } } private void copyFilters(IUAElement src) { UAElement sourceElement = (UAElement)src; String filter = sourceElement.getAttribute(ATTRIBUTE_FILTER); if (filter != null && filter.length() > 0) { this.setAttribute(ATTRIBUTE_FILTER, filter); } filters = sourceElement.getFilterElements(); this.enablementExpression = sourceElement.enablementExpression; this.src = sourceElement.src; } private Filter[] getFilterElements() { if (filters == null) { List<Filter> list = new ArrayList<>(); if (element.hasChildNodes()) { Node node = element.getFirstChild(); while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { String elementKind = node.getNodeName(); if (ExpressionTagNames.ENABLEMENT.equals(elementKind)) { Element enablement = (Element)node; try { enablementExpression = ExpressionConverter.getDefault().perform(enablement); } catch (CoreException e) { } } else if (ELEMENT_FILTER.equals(elementKind)) { Element filter = (Element)node; String filterName = filter.getAttribute(ATTRIBUTE_NAME); String value = filter.getAttribute(ATTRIBUTE_VALUE); if (filterName.length() > 0 && value.length() > 0) { boolean isNegated = false; if (value.startsWith("!")) { //$NON-NLS-1$ isNegated = true; value = value.substring(1); } if (filterName.length() > 0 && value.length() > 0) { list.add(new Filter(filterName, value, isNegated)); } } } } node = node.getNextSibling(); } } filters = list.toArray(new Filter[list.size()]); } return filters; } public void appendChild(UAElement uaElementToAppend) { importElement(uaElementToAppend); element.appendChild(uaElementToAppend.element); uaElementToAppend.parent = this; if (children != null) { children.add(uaElementToAppend); } } public void appendChildren(IUAElement[] children) { if (this.children == null && children.length > 0) { this.children = new ArrayList<>(4); } for (int i=0;i<children.length;i++) { appendChild(children[i] instanceof UAElement ? (UAElement)children[i] : UAElementFactory.newElement(children[i])); } } /* * This method is synchronized to fix Bug 232169. When modifying this source be careful not * to introduce any logic which could possibly cause this thread to block. */ synchronized public String getAttribute(String name) { String value = element.getAttribute(name); if (value != null && value.length() > 0) { return value; } return null; } /* * This method is synchronized to fix Bug 230037. A review of the code indicated that there was no * path which could get blocked and cause deadlock. When modifying this source be careful not * to introduce any logic which could possibly cause this thread to block. */ @Override public synchronized IUAElement[] getChildren() { if (children == null) { if (element.hasChildNodes()) { children = new ArrayList<>(4); Node node = element.getFirstChild(); while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { UAElement uaElement = UAElementFactory.newElement((Element)node); if (uaElement != null) { uaElement.parent = this; children.add(uaElement); } } node = node.getNextSibling(); } } else { return new UAElement[0]; } } return children.toArray(new UAElement[children.size()]); } @SuppressWarnings("unchecked") public <T> T[] getChildren(Class<T> clazz) { IUAElement[] children = getChildren(); if (children.length > 0) { List<Object> list = new ArrayList<>(); for (int i=0;i<children.length;++i) { IUAElement child = children[i]; if (clazz.isAssignableFrom(child.getClass())) { list.add(child); } } return list.toArray((T[]) Array.newInstance(clazz, list.size())); } return (T[]) Array.newInstance(clazz, 0); } public String getElementName() { return element.getNodeName(); } private static Document getDocument() { if (document == null) { if (builder == null) { try { builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); builder.setEntityResolver(new LocalEntityResolver()); } catch (ParserConfigurationException e) { String msg = "Error creating document builder"; //$NON-NLS-1$ HelpPlugin.logError(msg, e); } } document = builder.newDocument(); } return document; } public UAElement getParentElement() { return parent; } public void insertBefore(UAElement newChild, UAElement refChild) { importElement(newChild); element.insertBefore(newChild.element, refChild.element); newChild.parent = this; getChildren(); if (children != null) { int index = children.indexOf(refChild); if (index < 0) { // cache is now invalid children = null; } else { children.add(index, newChild); } } } @Override public boolean isEnabled(IEvaluationContext context) { if (!ProductPreferences.useEnablementFilters()) { return true; } if (src != null) { return src.isEnabled(context); } String filter = getAttribute(ATTRIBUTE_FILTER); if (filter != null) { return isEnabledByFilterAttribute(filter); } Filter[] filterElements = getFilterElements(); for (int i = 0; i < filterElements.length; i++) { if (!isFilterEnabled(filterElements[i])) { return false; } } if (enablementExpression != null) { try { return enablementExpression.evaluate(context) == EvaluationResult.TRUE; } catch (CoreException e) { return false; } } return true; } public void removeChild(UAElement elementToRemove) { element.removeChild(elementToRemove.element); elementToRemove.parent = null; if (children != null) { if (!children.remove(elementToRemove)) { // cache is now invalid children = null; } } } public void setAttribute(String name, String value) { element.setAttribute(name, value); } private void importElement(UAElement uaElementToImport) { Element elementToImport = uaElementToImport.element; Document ownerDocument = element.getOwnerDocument(); if (!ownerDocument.equals(elementToImport.getOwnerDocument()) ) { elementToImport = (Element)ownerDocument.importNode(elementToImport, true); uaElementToImport.children = null; } else { if (elementToImport.getParentNode() != null) { elementToImport = (Element)ownerDocument.importNode(elementToImport, true); uaElementToImport.children = null; } else { } } uaElementToImport.element = elementToImport; } private boolean isEnabledByFilterAttribute(String filter) { return !FilterResolver.getInstance().isFiltered(filter); } private boolean isFilterEnabled(Filter filter) { return !FilterResolver.getInstance().isFiltered(filter.name, filter.value, filter.isNegated); } public Element getElement() { return element; } }