package org.eclipse.core.internal.registry;
import java.io.IOException;
import java.util.*;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
public class ExtensionsParser extends DefaultHandler {
private final static String NO_EXTENSION_MUNGING = "eclipse.noExtensionMunging";
private static final String VERSION_3_0 = "3.0";
private static final String VERSION_3_2 = "3.2";
private static Map<String, String> extensionPointMap;
static {
initializeExtensionPointMap();
}
private static void initializeExtensionPointMap() {
Map<String, String> map = new HashMap<>(13);
map.put("org.eclipse.ui.markerImageProvider", "org.eclipse.ui.ide.markerImageProvider");
map.put("org.eclipse.ui.markerHelp", "org.eclipse.ui.ide.markerHelp");
map.put("org.eclipse.ui.markerImageProviders", "org.eclipse.ui.ide.markerImageProviders");
map.put("org.eclipse.ui.markerResolution", "org.eclipse.ui.ide.markerResolution");
map.put("org.eclipse.ui.projectNatureImages", "org.eclipse.ui.ide.projectNatureImages");
map.put("org.eclipse.ui.resourceFilters", "org.eclipse.ui.ide.resourceFilters");
map.put("org.eclipse.ui.markerUpdaters", "org.eclipse.ui.editors.markerUpdaters");
map.put("org.eclipse.ui.documentProviders", "org.eclipse.ui.editors.documentProviders");
map.put("org.eclipse.ui.workbench.texteditor.markerAnnotationSpecification", "org.eclipse.ui.editors.markerAnnotationSpecification");
map.put("org.eclipse.help.browser", "org.eclipse.help.base.browser");
map.put("org.eclipse.help.luceneAnalyzer", "org.eclipse.help.base.luceneAnalyzer");
map.put("org.eclipse.help.webapp", "org.eclipse.help.base.webapp");
map.put("org.eclipse.help.support", "org.eclipse.ui.helpSupport");
extensionPointMap = map;
}
private static long cumulativeTime = 0;
private boolean compatibilityMode;
private String locationName = null;
private final Stack<Integer> stateStack = new Stack<>();
private final Stack<KeyedElement> objectStack = new Stack<>();
private String schemaVersion = null;
private final MultiStatus status;
private final ExtensionRegistry registry;
protected ResourceBundle resources;
private RegistryObjectManager objectManager;
private Contribution contribution;
private String configurationElementValue;
public static final int PARSE_PROBLEM = 1;
public static final String PLUGIN = "plugin";
public static final String PLUGIN_ID = "id";
public static final String PLUGIN_NAME = "name";
public static final String FRAGMENT = "fragment";
public static final String BUNDLE_UID = "id";
public static final String EXTENSION_POINT = "extension-point";
public static final String EXTENSION_POINT_NAME = "name";
public static final String EXTENSION_POINT_ID = "id";
public static final String EXTENSION_POINT_SCHEMA = "schema";
public static final String EXTENSION = "extension";
public static final String EXTENSION_NAME = "name";
public static final String EXTENSION_ID = "id";
public static final String EXTENSION_TARGET = "point";
public static final String ELEMENT = "element";
public static final String ELEMENT_NAME = "name";
public static final String ELEMENT_VALUE = "value";
public static final String PROPERTY = "property";
public static final String PROPERTY_NAME = "name";
public static final String PROPERTY_VALUE = "value";
private static final int IGNORED_ELEMENT_STATE = 0;
private static final int INITIAL_STATE = 1;
private static final int BUNDLE_STATE = 2;
private static final int BUNDLE_EXTENSION_POINT_STATE = 5;
private static final int BUNDLE_EXTENSION_STATE = 6;
private static final int CONFIGURATION_ELEMENT_STATE = 10;
private static final int EXTENSION_POINT_INDEX = 0;
private static final int EXTENSION_INDEX = 1;
private static final int LAST_INDEX = 1;
@SuppressWarnings("unchecked")
private final ArrayList<RegistryObject> scratchVectors[] = new ArrayList[LAST_INDEX + 1];
private Locator locator = null;
private boolean = false;
private ArrayList<String> processedExtensionIds = null;
private final ArrayList<RegistryObject> addedRegistryObjects = new ArrayList<>(5);
public ExtensionsParser(MultiStatus status, ExtensionRegistry registry) {
super();
this.status = status;
this.registry = registry;
}
@Override
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
@Override
public void characters(char[] ch, int start, int length) {
int state = stateStack.peek().intValue();
if (state != CONFIGURATION_ELEMENT_STATE)
return;
if (state == CONFIGURATION_ELEMENT_STATE) {
ConfigurationElement currentConfigElement = (ConfigurationElement) objectStack.peek();
String value = new String(ch, start, length);
if (configurationElementValue == null) {
if (value.trim().length() != 0) {
configurationElementValue = value;
}
} else {
configurationElementValue = configurationElementValue + value;
}
if (configurationElementValue != null)
currentConfigElement.setValue(configurationElementValue);
}
}
@Override
public void endDocument() {
}
@Override
public void endElement(String uri, String elementName, String qName) {
switch (stateStack.peek().intValue()) {
case IGNORED_ELEMENT_STATE :
stateStack.pop();
break;
case INITIAL_STATE :
internalError(NLS.bind(RegistryMessages.parse_internalStack, elementName));
break;
case BUNDLE_STATE :
stateStack.pop();
ArrayList<?> extensionPoints = scratchVectors[EXTENSION_POINT_INDEX];
ArrayList<?> extensions = scratchVectors[EXTENSION_INDEX];
int[] namespaceChildren = new int[2 + extensionPoints.size() + extensions.size()];
int position = 2;
if (extensionPoints.size() > 0) {
namespaceChildren[Contribution.EXTENSION_POINT] = extensionPoints.size();
for (Iterator<?> iter = extensionPoints.iterator(); iter.hasNext();) {
namespaceChildren[position++] = ((RegistryObject) iter.next()).getObjectId();
}
extensionPoints.clear();
}
if (extensions.size() > 0) {
Extension[] renamedExtensions = fixRenamedExtensionPoints(extensions.toArray(new Extension[extensions.size()]));
namespaceChildren[Contribution.EXTENSION] = renamedExtensions.length;
for (Extension renamedExtension : renamedExtensions) {
namespaceChildren[position++] = renamedExtension.getObjectId();
}
extensions.clear();
}
contribution.setRawChildren(namespaceChildren);
break;
case BUNDLE_EXTENSION_POINT_STATE :
if (elementName.equals(EXTENSION_POINT)) {
stateStack.pop();
}
break;
case BUNDLE_EXTENSION_STATE :
if (elementName.equals(EXTENSION)) {
stateStack.pop();
Extension currentExtension = (Extension) objectStack.pop();
if (currentExtension.getNamespaceIdentifier() == null)
currentExtension.setNamespaceIdentifier(contribution.getDefaultNamespace());
currentExtension.setContributorId(contribution.getContributorId());
scratchVectors[EXTENSION_INDEX].add(currentExtension);
}
break;
case CONFIGURATION_ELEMENT_STATE :
stateStack.pop();
configurationElementValue = null;
ConfigurationElement currentConfigElement = (ConfigurationElement) objectStack.pop();
String value = currentConfigElement.getValueAsIs();
if (value != null) {
currentConfigElement.setValue(translate(value).trim());
}
RegistryObject parent = (RegistryObject) objectStack.peek();
int[] oldValues = parent.getRawChildren();
int size = oldValues.length;
int[] newValues = new int[size + 1];
for (int i = 0; i < size; i++) {
newValues[i] = oldValues[i];
}
newValues[size] = currentConfigElement.getObjectId();
parent.setRawChildren(newValues);
currentConfigElement.setParentId(parent.getObjectId());
currentConfigElement.setParentType(parent instanceof ConfigurationElement ? RegistryObjectManager.CONFIGURATION_ELEMENT : RegistryObjectManager.EXTENSION);
break;
}
}
@Override
public void error(SAXParseException ex) {
logStatus(ex);
}
@Override
public void fatalError(SAXParseException ex) throws SAXException {
cleanup();
logStatus(ex);
throw ex;
}
private void cleanup() {
for (RegistryObject object : addedRegistryObjects) {
if (object instanceof ExtensionPoint) {
String id = ((ExtensionPoint) object).getUniqueIdentifier();
objectManager.removeExtensionPoint(id);
} else
objectManager.remove(object.getObjectId(), true);
}
}
private void handleExtensionPointState(String elementName) {
stateStack.push(Integer.valueOf(IGNORED_ELEMENT_STATE));
unknownElement(EXTENSION_POINT, elementName);
}
private void handleExtensionState(String elementName, Attributes attributes) {
stateStack.push(Integer.valueOf(CONFIGURATION_ELEMENT_STATE));
configurationElementValue = null;
ConfigurationElement currentConfigurationElement = registry.getElementFactory().createConfigurationElement(contribution.shouldPersist());
currentConfigurationElement.setContributorId(contribution.getContributorId());
objectStack.push(currentConfigurationElement);
currentConfigurationElement.setName(elementName);
parseConfigurationElementAttributes(attributes);
objectManager.add(currentConfigurationElement, true);
addedRegistryObjects.add(currentConfigurationElement);
}
private void handleInitialState(String elementName, Attributes attributes) {
compatibilityMode = attributes.getLength() > 0;
stateStack.push(Integer.valueOf(BUNDLE_STATE));
objectStack.push(contribution);
}
private void handleBundleState(String elementName, Attributes attributes) {
if (elementName.equals(EXTENSION_POINT)) {
stateStack.push(Integer.valueOf(BUNDLE_EXTENSION_POINT_STATE));
parseExtensionPointAttributes(attributes);
return;
}
if (elementName.equals(EXTENSION)) {
stateStack.push(Integer.valueOf(BUNDLE_EXTENSION_STATE));
parseExtensionAttributes(attributes);
return;
}
stateStack.push(Integer.valueOf(IGNORED_ELEMENT_STATE));
if (!compatibilityMode)
unknownElement(PLUGIN, elementName);
}
private void logStatus(SAXParseException ex) {
String name = ex.getSystemId();
if (name == null)
name = locationName;
if (name == null)
name = "";
else
name = name.substring(1 + name.lastIndexOf("/"));
String msg;
if (name.equals(""))
msg = NLS.bind(RegistryMessages.parse_error, ex.getMessage());
else
msg = NLS.bind(RegistryMessages.parse_errorNameLineColumn, (new Object[] {name, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber()), ex.getMessage()}));
error(new Status(IStatus.WARNING, RegistryMessages.OWNER_NAME, PARSE_PROBLEM, msg, ex));
}
public Contribution parseManifest(SAXParserFactory factory, InputSource in, String manifestName, RegistryObjectManager registryObjects, Contribution currentNamespace, ResourceBundle bundle) throws ParserConfigurationException, SAXException, IOException {
long start = 0;
this.resources = bundle;
this.objectManager = registryObjects;
this.contribution = currentNamespace;
if (registry.debug())
start = System.currentTimeMillis();
if (factory == null)
throw new SAXException(RegistryMessages.parse_xmlParserNotAvailable);
try {
locationName = in.getSystemId();
if (locationName == null)
locationName = manifestName;
factory.setNamespaceAware(true);
try {
factory.setFeature("http://xml.org/sax/features/string-interning", true);
} catch (SAXException se) {
}
factory.setValidating(false);
factory.newSAXParser().parse(in, this);
return (Contribution) objectStack.pop();
} finally {
if (registry.debug()) {
cumulativeTime = cumulativeTime + (System.currentTimeMillis() - start);
System.out.println("Cumulative parse time so far : " + cumulativeTime);
}
}
}
private void parseConfigurationElementAttributes(Attributes attributes) {
ConfigurationElement parentConfigurationElement = (ConfigurationElement) objectStack.peek();
int len = (attributes != null) ? attributes.getLength() : 0;
if (len == 0) {
parentConfigurationElement.setProperties(RegistryObjectManager.EMPTY_STRING_ARRAY);
return;
}
String[] properties = new String[len * 2];
for (int i = 0; i < len; i++) {
properties[i * 2] = attributes.getLocalName(i);
properties[i * 2 + 1] = translate(attributes.getValue(i));
}
parentConfigurationElement.setProperties(properties);
properties = null;
}
private void parseExtensionAttributes(Attributes attributes) {
Extension currentExtension = registry.getElementFactory().createExtension(contribution.shouldPersist());
objectStack.push(currentExtension);
String simpleId = null;
String namespaceName = null;
int len = (attributes != null) ? attributes.getLength() : 0;
for (int i = 0; i < len; i++) {
String attrName = attributes.getLocalName(i);
String attrValue = attributes.getValue(i).trim();
if (attrName.equals(EXTENSION_NAME))
currentExtension.setLabel(translate(attrValue));
else if (attrName.equals(EXTENSION_ID)) {
int simpleIdStart = attrValue.lastIndexOf('.');
if ((simpleIdStart != -1) && extractNamespaces) {
simpleId = attrValue.substring(simpleIdStart + 1);
namespaceName = attrValue.substring(0, simpleIdStart);
} else {
simpleId = attrValue;
namespaceName = contribution.getDefaultNamespace();
}
currentExtension.setSimpleIdentifier(simpleId);
currentExtension.setNamespaceIdentifier(namespaceName);
} else if (attrName.equals(EXTENSION_TARGET)) {
String targetName;
if (attrValue.lastIndexOf('.') == -1) {
String baseId = contribution.getDefaultNamespace();
targetName = baseId + '.' + attrValue;
} else
targetName = attrValue;
currentExtension.setExtensionPointIdentifier(targetName);
} else
unknownAttribute(attrName, EXTENSION);
}
if (currentExtension.getExtensionPointIdentifier() == null) {
missingAttribute(EXTENSION_TARGET, EXTENSION);
stateStack.pop();
stateStack.push(Integer.valueOf(IGNORED_ELEMENT_STATE));
objectStack.pop();
return;
}
if (simpleId != null && registry.debug()) {
String uniqueId = namespaceName + '.' + simpleId;
IExtension existingExtension = registry.getExtension(uniqueId);
if (existingExtension != null) {
String currentSupplier = contribution.getDefaultNamespace();
String existingSupplier = existingExtension.getContributor().getName();
String msg = NLS.bind(RegistryMessages.parse_duplicateExtension, new String[] {currentSupplier, existingSupplier, uniqueId});
registry.log(new Status(IStatus.WARNING, RegistryMessages.OWNER_NAME, 0, msg, null));
} else if (processedExtensionIds != null) {
for (Iterator<String> i = processedExtensionIds.iterator(); i.hasNext();) {
if (uniqueId.equals(i.next())) {
String currentSupplier = contribution.getDefaultNamespace();
String existingSupplier = currentSupplier;
String msg = NLS.bind(RegistryMessages.parse_duplicateExtension, new String[] {currentSupplier, existingSupplier, uniqueId});
registry.log(new Status(IStatus.WARNING, RegistryMessages.OWNER_NAME, 0, msg, null));
break;
}
}
}
if (processedExtensionIds == null)
processedExtensionIds = new ArrayList<>(10);
processedExtensionIds.add(uniqueId);
}
objectManager.add(currentExtension, true);
addedRegistryObjects.add(currentExtension);
}
private void missingAttribute(String attribute, String element) {
if (locator == null)
internalError(NLS.bind(RegistryMessages.parse_missingAttribute, attribute, element));
else
internalError(NLS.bind(RegistryMessages.parse_missingAttributeLine, (new String[] {attribute, element, Integer.toString(locator.getLineNumber())})));
}
private void unknownAttribute(String attribute, String element) {
if (locator == null)
internalError(NLS.bind(RegistryMessages.parse_unknownAttribute, attribute, element));
else
internalError(NLS.bind(RegistryMessages.parse_unknownAttributeLine, (new String[] {attribute, element, Integer.toString(locator.getLineNumber())})));
}
private void unknownElement(String parent, String element) {
if (locator == null)
internalError(NLS.bind(RegistryMessages.parse_unknownElement, element, parent));
else
internalError(NLS.bind(RegistryMessages.parse_unknownElementLine, (new String[] {element, parent, Integer.toString(locator.getLineNumber())})));
}
private void parseExtensionPointAttributes(Attributes attributes) {
ExtensionPoint currentExtPoint = registry.getElementFactory().createExtensionPoint(contribution.shouldPersist());
int len = (attributes != null) ? attributes.getLength() : 0;
for (int i = 0; i < len; i++) {
String attrName = attributes.getLocalName(i);
String attrValue = attributes.getValue(i).trim();
if (attrName.equals(EXTENSION_POINT_NAME))
currentExtPoint.setLabel(translate(attrValue));
else if (attrName.equals(EXTENSION_POINT_ID)) {
String uniqueId;
String namespaceName;
int simpleIdStart = attrValue.lastIndexOf('.');
if (simpleIdStart != -1 && extractNamespaces) {
namespaceName = attrValue.substring(0, simpleIdStart);
uniqueId = attrValue;
} else {
namespaceName = contribution.getDefaultNamespace();
uniqueId = namespaceName + '.' + attrValue;
}
currentExtPoint.setUniqueIdentifier(uniqueId);
currentExtPoint.setNamespace(namespaceName);
} else if (attrName.equals(EXTENSION_POINT_SCHEMA))
currentExtPoint.setSchema(attrValue);
else
unknownAttribute(attrName, EXTENSION_POINT);
}
if (currentExtPoint.getSimpleIdentifier() == null || currentExtPoint.getLabel() == null) {
String attribute = currentExtPoint.getSimpleIdentifier() == null ? EXTENSION_POINT_ID : EXTENSION_POINT_NAME;
missingAttribute(attribute, EXTENSION_POINT);
stateStack.pop();
stateStack.push(Integer.valueOf(IGNORED_ELEMENT_STATE));
return;
}
if (!objectManager.addExtensionPoint(currentExtPoint, true)) {
if (registry.debug()) {
String msg = NLS.bind(RegistryMessages.parse_duplicateExtensionPoint, currentExtPoint.getUniqueIdentifier(), contribution.getDefaultNamespace());
registry.log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, 0, msg, null));
}
stateStack.pop();
stateStack.push(Integer.valueOf(IGNORED_ELEMENT_STATE));
return;
}
if (currentExtPoint.getNamespace() == null)
currentExtPoint.setNamespace(contribution.getDefaultNamespace());
currentExtPoint.setContributorId(contribution.getContributorId());
addedRegistryObjects.add(currentExtPoint);
scratchVectors[EXTENSION_POINT_INDEX].add(currentExtPoint);
}
@Override
public void startDocument() {
stateStack.push(Integer.valueOf(INITIAL_STATE));
for (int i = 0; i <= LAST_INDEX; i++) {
scratchVectors[i] = new ArrayList<>();
}
}
@Override
public void startElement(String uri, String elementName, String qName, Attributes attributes) {
switch (stateStack.peek().intValue()) {
case INITIAL_STATE :
handleInitialState(elementName, attributes);
break;
case BUNDLE_STATE :
handleBundleState(elementName, attributes);
break;
case BUNDLE_EXTENSION_POINT_STATE :
handleExtensionPointState(elementName);
break;
case BUNDLE_EXTENSION_STATE :
case CONFIGURATION_ELEMENT_STATE :
handleExtensionState(elementName, attributes);
break;
default :
stateStack.push(Integer.valueOf(IGNORED_ELEMENT_STATE));
if (!compatibilityMode)
internalError(NLS.bind(RegistryMessages.parse_unknownTopElement, elementName));
}
}
@Override
public void warning(SAXParseException ex) {
logStatus(ex);
}
private void internalError(String message) {
error(new Status(IStatus.WARNING, RegistryMessages.OWNER_NAME, PARSE_PROBLEM, message, null));
}
@Override
public void processingInstruction(String target, String data) {
if (target.equalsIgnoreCase("eclipse")) {
schemaVersion = VERSION_3_0;
StringTokenizer tokenizer = new StringTokenizer(data, "=\"");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.equalsIgnoreCase("version")) {
if (!tokenizer.hasMoreTokens()) {
break;
}
schemaVersion = tokenizer.nextToken();
break;
}
}
initializeExtractNamespace();
}
}
public void error(IStatus error) {
status.add(error);
}
protected String translate(String key) {
return registry.translate(key, resources);
}
private Extension[] fixRenamedExtensionPoints(Extension[] extensions) {
if (extensions == null || versionAtLeast(VERSION_3_0) || RegistryProperties.getProperty(NO_EXTENSION_MUNGING) != null)
return extensions;
for (Extension extension : extensions) {
String oldPointId = extension.getExtensionPointIdentifier();
String newPointId = extensionPointMap.get(oldPointId);
if (newPointId != null) {
extension.setExtensionPointIdentifier(newPointId);
}
}
return extensions;
}
private void () {
extractNamespaces = Boolean.valueOf(versionAtLeast(VERSION_3_2)).booleanValue();
}
private boolean versionAtLeast(String testVersion) {
if (schemaVersion == null)
return false;
StringTokenizer testVersionTokenizer = new StringTokenizer(testVersion, ".");
StringTokenizer schemaVersionTokenizer = new StringTokenizer(schemaVersion, ".");
while (testVersionTokenizer.hasMoreTokens() && schemaVersionTokenizer.hasMoreTokens()) {
try {
if (Integer.parseInt(schemaVersionTokenizer.nextToken()) < Integer.parseInt(testVersionTokenizer.nextToken()))
return false;
} catch (NumberFormatException e) {
return false;
}
}
return true;
}
}