blob: b319a46409a0905fcd08ff00e9640674b840362d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2013 GEBIT Gesellschaft fuer EDV-Beratung
* und Informatik-Technologien mbH,
* Berlin, Duesseldorf, Frankfurt (Germany) and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation
* IBM Corporation - bug fixes
*******************************************************************************/
package org.eclipse.ant.internal.ui.editor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.ant.internal.core.IAntCoreConstants;
import org.eclipse.ant.internal.ui.AntUIPlugin;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* The <code>TaskDescriptionProvider</code> provides the additional descriptions for tasks and attributes for the code assist.
* <p>
* Descriptions for task are originally provided with the XML file <code>TASKS_DESCRIPTION_XML_FILE_NAME</code>. This file is parsed by the provider
* and requested descriptions are returned.
* </p>
*/
public class TaskDescriptionProvider {
/**
* The file that contains all task descriptions.
*/
public static final String TASKS_DESCRIPTION_XML_FILE_NAME = "/org/eclipse/ant/internal/ui/editor/anttasks_1.6.0.xml"; //$NON-NLS-1$
public static final String XML_TAG_TASKS = "tasks"; //$NON-NLS-1$
public static final String XML_TAG_TASK = "task"; //$NON-NLS-1$
public static final String XML_TAG_ELEMENTS = "elements"; //$NON-NLS-1$
public static final String XML_TAG_ATTRIBUTE = "attribute"; //$NON-NLS-1$
public static final String XML_TAG_ATTRIBUTES = "attributes"; //$NON-NLS-1$
public static final String XML_TAG_ELEMENT = "element"; //$NON-NLS-1$
public static final String XML_TAG_STRUCTURE = "structure"; //$NON-NLS-1$
public static final String XML_ATTRIBUTE_REQUIRED = "required"; //$NON-NLS-1$
/**
* Class to avoid holding on to DOM element handles
*
* @since 3.5
*/
static class ProposalNode {
String desc = null;
String required = null;
HashMap<String, ProposalNode> nodes = null;
ProposalNode(String desc, String required) {
this.desc = desc;
this.required = required;
}
void addChild(String name, ProposalNode node) {
if (nodes == null) {
nodes = new HashMap<>(9);
}
nodes.put(name, node);
}
ProposalNode getChild(String name) {
if (nodes != null) {
return nodes.get(name);
}
return null;
}
}
private static TaskDescriptionProvider fgDefault;
/**
* Mapping of {@link String} to {@link ProposalNode} <br>
* <br>
* <code>Map&lt;String, ProposalNode&gt;</code>
*/
private Map<String, ProposalNode> taskNodes = null;
/**
* Meant to be a singleton
*/
private TaskDescriptionProvider() {
}
public static TaskDescriptionProvider getDefault() {
if (fgDefault == null) {
fgDefault = new TaskDescriptionProvider();
IRunnableWithProgress runnable = monitor -> fgDefault.initialize();
IProgressService service = PlatformUI.getWorkbench().getProgressService();
try {
service.busyCursorWhile(runnable);
}
catch (InvocationTargetException e) {
// do nothing
}
catch (InterruptedException e) {
// do nothing
}
}
return fgDefault;
}
/**
* Parses the task description XML file and stores the information.
*/
protected void initialize() {
taskNodes = new HashMap<>();
Document doc = parseFile(TASKS_DESCRIPTION_XML_FILE_NAME);
Node root = doc.getDocumentElement();
NodeList tasks = root.getChildNodes();
Node node = null;
for (int i = 0; i < tasks.getLength(); i++) {
node = tasks.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (XML_TAG_TASK.equals(node.getNodeName())) {
Element task = (Element) node;
String name = task.getAttribute(IAntCoreConstants.NAME);
if (name != null) {
ProposalNode tasknode = new ProposalNode(getDescription(task), null);
taskNodes.put(name, tasknode);
NodeList nodes = task.getElementsByTagName(XML_TAG_ATTRIBUTE);
Element e = null;
for (int j = 0; j < nodes.getLength(); j++) {
e = (Element) nodes.item(j);
addNode(e, tasknode);
}
nodes = task.getElementsByTagName(XML_TAG_ELEMENT);
for (int j = 0; j < nodes.getLength(); j++) {
e = (Element) nodes.item(j);
addNode(e, tasknode);
}
}
}
}
}
}
/**
* Adds a new child {@link ProposalNode} to the given parent node
*
* @param element
* @param node
* @since 3.5
*/
void addNode(Element element, ProposalNode node) {
String name = element.getAttribute(IAntCoreConstants.NAME);
if (name != null) {
node.addChild(name, new ProposalNode(getDescription(element), element.getAttribute(XML_ATTRIBUTE_REQUIRED)));
}
}
/**
* Recursively find the description text for the parent {@link Element}
*
* @param element
* @return the description element text or <code>null</code>
* @since 3.5
*/
String getDescription(Element element) {
NodeList nodes = element.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE && IAntCoreConstants.DESCRIPTION.equals(node.getNodeName())) {
node = node.getFirstChild();
if (node != null) {
return node.getNodeValue();
}
}
}
return null;
}
/**
* Returns the (DOM) document as a result of parsing the file with the specified file name.
* <P>
* The file will be loaded as resource, thus must begin with '/' and must be relative to the classpath.
*/
private Document parseFile(String aFileName) {
Document tempDocument = null;
DocumentBuilderFactory tempFactory = DocumentBuilderFactory.newInstance();
tempFactory.setIgnoringComments(true);
tempFactory.setIgnoringElementContentWhitespace(true);
tempFactory.setCoalescing(true);
try {
DocumentBuilder tempDocBuilder = tempFactory.newDocumentBuilder();
tempDocBuilder.setErrorHandler(new DefaultHandler());
URL tempURL = getClass().getResource(aFileName);
InputSource tempInputSource = new InputSource(tempURL.toExternalForm());
tempDocument = tempDocBuilder.parse(tempInputSource);
}
catch (ParserConfigurationException e) {
AntUIPlugin.log(e);
}
catch (IOException ioException) {
AntUIPlugin.log(ioException);
}
catch (SAXException saxException) {
AntUIPlugin.log(saxException);
}
return tempDocument;
}
/**
* Returns the description string for the specified task.
*
* @return description string or <code>null</code> if task not known or no description available.
*/
public String getDescriptionForTask(String aTaskName) {
ProposalNode task = taskNodes.get(aTaskName);
if (task != null) {
return task.desc;
}
return null;
}
/**
* Returns the description string for the specified attribute of the specified task.
*
* @return description string or <code>null</code> if task or attribute not known or no description available.
*/
public String getDescriptionForTaskAttribute(String aTaskName, String anAttributeName) {
ProposalNode task = taskNodes.get(aTaskName);
if (task != null) {
ProposalNode att = task.getChild(anAttributeName);
if (att != null) {
return att.desc;
}
}
return null;
}
/**
* Returns the required string value for the specified attribute of the specified task.
*
* @return required string or <code>null</code> if task or attribute not known or no description available.
*/
public String getRequiredAttributeForTaskAttribute(String aTaskName, String anAttributeName) {
ProposalNode task = taskNodes.get(aTaskName);
if (task != null) {
ProposalNode att = task.getChild(anAttributeName);
if (att != null) {
return att.required;
}
}
return null;
}
/**
* Returns the {@link ProposalNode} for the given task name or <code>null</code> if one does not exist
*
* @param aTaskName
* @return the {@link ProposalNode} for the given name or <code>null</code>
* @since 3.5
*/
ProposalNode getTaskNode(String aTaskName) {
return taskNodes.get(aTaskName);
}
protected static void reset() {
if (fgDefault != null && fgDefault.taskNodes != null) {
fgDefault.taskNodes.clear();
}
fgDefault = null;
}
}