1 /*******************************************************************************
2  *  Copyright (c) 2005, 2017 IBM Corporation and others.
3  *
4  *  This program and the accompanying materials
5  *  are made available under the terms of the Eclipse Public License 2.0
6  *  which accompanies this distribution, and is available at
7  *  https://www.eclipse.org/legal/epl-2.0/
8  *
9  *  SPDX-License-Identifier: EPL-2.0
10  *
11  *  Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.internal.core.plugin;
15 
16 import java.io.StringReader;
17 import java.util.Stack;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20 import javax.xml.parsers.DocumentBuilderFactory;
21 import javax.xml.parsers.ParserConfigurationException;
22 import org.eclipse.pde.internal.core.TargetPlatformHelper;
23 import org.eclipse.pde.internal.core.util.IdUtil;
24 import org.w3c.dom.DOMException;
25 import org.w3c.dom.Document;
26 import org.w3c.dom.Element;
27 import org.w3c.dom.Node;
28 import org.w3c.dom.Text;
29 import org.xml.sax.Attributes;
30 import org.xml.sax.InputSource;
31 import org.xml.sax.Locator;
32 import org.xml.sax.SAXException;
33 import org.xml.sax.helpers.DefaultHandler;
34 
35 public class PluginHandler extends DefaultHandler {
36 	private Document fDocument;
37 	private Element fRootElement;
38 	private final Stack<Element> fOpenElements = new Stack<>();
39 
40 	private String fSchemaVersion;
41 	private final boolean fAbbreviated;
42 	private Locator fLocator;
43 	private boolean fPop;
44 
45 	private static final Pattern VERSION_RE = Pattern.compile("version\\s*=\\s*\"([^\"]+)\""); //$NON-NLS-1$
46 
PluginHandler(boolean abbreviated)47 	public PluginHandler(boolean abbreviated) {
48 		fAbbreviated = abbreviated;
49 	}
50 
51 	@Override
startElement(String uri, String localName, String qName, Attributes attributes)52 	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
53 
54 		fPop = true;
55 
56 		if (fAbbreviated && fOpenElements.size() == 2) {
57 			Element parent = fOpenElements.peek();
58 			if (parent.getNodeName().equals("extension") && !isInterestingExtension(fOpenElements.peek())) { //$NON-NLS-1$
59 				fPop = false;
60 				return;
61 			}
62 		}
63 
64 		Element element = fDocument.createElement(qName);
65 		for (int i = 0; i < attributes.getLength(); i++) {
66 			element.setAttribute(attributes.getQName(i), attributes.getValue(i));
67 			if ("extension".equals(qName) || "extension-point".equals(qName)) { //$NON-NLS-1$ //$NON-NLS-2$
68 				element.setAttribute("line", Integer.toString(fLocator.getLineNumber())); //$NON-NLS-1$
69 			}
70 		}
71 
72 		if (fRootElement == null) {
73 			fRootElement = element;
74 		} else {
75 			fOpenElements.peek().appendChild(element);
76 		}
77 
78 		fOpenElements.push(element);
79 	}
80 
isInterestingExtension(Element element)81 	protected boolean isInterestingExtension(Element element) {
82 		String point = element.getAttribute("point"); //$NON-NLS-1$
83 		return IdUtil.isInterestingExtensionPoint(point);
84 	}
85 
86 	@Override
endElement(String uri, String localName, String qName)87 	public void endElement(String uri, String localName, String qName) throws SAXException {
88 		if (fPop || (qName.equals("extension") && fOpenElements.size() == 2)) { //$NON-NLS-1$
89 			fOpenElements.pop();
90 		}
91 	}
92 
93 	@Override
setDocumentLocator(Locator locator)94 	public void setDocumentLocator(Locator locator) {
95 		fLocator = locator;
96 	}
97 
98 	@Override
startDocument()99 	public void startDocument() throws SAXException {
100 		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
101 		try {
102 			fDocument = factory.newDocumentBuilder().newDocument();
103 		} catch (ParserConfigurationException e) {
104 		}
105 	}
106 
107 	@Override
endDocument()108 	public void endDocument() throws SAXException {
109 		fDocument.appendChild(fRootElement);
110 	}
111 
112 	@Override
processingInstruction(String target, String data)113 	public void processingInstruction(String target, String data) throws SAXException {
114 		if ("eclipse".equals(target)) { //$NON-NLS-1$
115 			// Data should be of the form: version="<version>"
116 			data = data.trim();
117 			Matcher matcher = VERSION_RE.matcher(data);
118 			if (matcher.matches()) {
119 				fSchemaVersion = TargetPlatformHelper.getSchemaVersionForTargetVersion(matcher.group(1));
120 			} else {
121 				fSchemaVersion = TargetPlatformHelper.getSchemaVersion();
122 			}
123 		}
124 	}
125 
126 	@Override
characters(char[] characters, int start, int length)127 	public void characters(char[] characters, int start, int length) throws SAXException {
128 		if (fAbbreviated) {
129 			return;
130 		}
131 
132 		processCharacters(characters, start, length);
133 	}
134 
135 	/**
136 	 * @param characters
137 	 * @param start
138 	 * @param length
139 	 * @throws DOMException
140 	 */
processCharacters(char[] characters, int start, int length)141 	protected void processCharacters(char[] characters, int start, int length) throws DOMException {
142 		StringBuilder buff = new StringBuilder();
143 		for (int i = 0; i < length; i++) {
144 			buff.append(characters[start + i]);
145 		}
146 		Text text = fDocument.createTextNode(buff.toString());
147 		if (fRootElement == null) {
148 			fDocument.appendChild(text);
149 		} else {
150 			fOpenElements.peek().appendChild(text);
151 		}
152 	}
153 
getDocumentElement()154 	public Node getDocumentElement() {
155 		if (fRootElement != null) {
156 			fRootElement.normalize();
157 		}
158 		return fRootElement;
159 	}
160 
getSchemaVersion()161 	public String getSchemaVersion() {
162 		return fSchemaVersion;
163 	}
164 
165 	@Override
resolveEntity(String publicId, String systemId)166 	public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
167 		// Prevent the resolution of external entities in order to
168 		// prevent the parser from accessing the Internet
169 		// This will prevent huge workbench performance degradations and hangs
170 		return new InputSource(new StringReader("")); //$NON-NLS-1$
171 	}
172 
173 }
174