blob: 7aaf1b9a8408c69c77e5f88bf2a5482a9d7f063f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 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.pde.internal.builders;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.plugin.*;
import org.eclipse.pde.internal.*;
import org.eclipse.pde.internal.core.*;
import org.eclipse.pde.internal.core.ischema.*;
import org.eclipse.pde.internal.core.schema.*;
import org.eclipse.pde.internal.core.util.IdUtil;
import org.w3c.dom.*;
import org.xml.sax.*;
public class ExtensionsErrorReporter extends ManifestErrorReporter {
IPluginModelBase fModel;
public ExtensionsErrorReporter(IFile file) {
super(file);
fModel = PDECore.getDefault().getModelManager().findModel(file.getProject());
}
/* (non-Javadoc)
* @see org.eclipse.pde.internal.builders.XMLErrorReporter#characters(char[], int, int)
*/
public void characters(char[] characters, int start, int length)
throws SAXException {
}
public void validateContent(IProgressMonitor monitor) {
Element element = getDocumentRoot();
if (element == null)
return;
String elementName = element.getNodeName();
if (!"plugin".equals(elementName) && !"fragment".equals(elementName)) { //$NON-NLS-1$ //$NON-NLS-2$
reportIllegalElement(element, CompilerFlags.ERROR);
} else {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_DEPRECATED);
if (severity != CompilerFlags.IGNORE) {
NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
reportUnusedAttribute(element, attrs.item(i).getNodeName(), severity);
}
}
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
if (monitor.isCanceled())
break;
Element child = (Element)children.item(i);
String name = child.getNodeName();
if (name.equals("extension")) { //$NON-NLS-1$
validateExtension(child);
} else if (name.equals("extension-point")) { //$NON-NLS-1$
validateExtensionPoint(child);
} else {
if (!name.equals("runtime") && !name.equals("requires")) { //$NON-NLS-1$ //$NON-NLS-2$
severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_ELEMENT);
if (severity != CompilerFlags.IGNORE)
reportIllegalElement(child, severity);
} else {
severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_DEPRECATED);
if (severity != CompilerFlags.IGNORE)
reportUnusedElement(child, severity);
}
}
}
}
}
protected void validateExtension(Element element) {
if (!assertAttributeDefined(element, "point", CompilerFlags.ERROR)) //$NON-NLS-1$
return;
String pointID = element.getAttribute("point"); //$NON-NLS-1$
IPluginExtensionPoint point = PDECore.getDefault().findExtensionPoint(pointID);
if (point == null) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNRESOLVED_EX_POINTS);
if (severity != CompilerFlags.IGNORE) {
report(NLS.bind(PDEMessages.Builders_Manifest_ex_point, pointID), //$NON-NLS-1$
getLine(element, "point"), severity); //$NON-NLS-1$
}
} else {
SchemaRegistry registry = PDECore.getDefault().getSchemaRegistry();
ISchema schema = registry.getSchema(pointID);
if (schema != null) {
validateElement(element, schema);
}
}
}
protected void validateElement(Element element, ISchema schema) {
String elementName = element.getNodeName();
ISchemaElement schemaElement = schema.findElement(elementName);
ISchemaElement parentSchema = null;
if (!"extension".equals(elementName)) { //$NON-NLS-1$
Node parent = element.getParentNode();
parentSchema = schema.findElement(parent.getNodeName());
}
if (parentSchema != null) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_ELEMENT);
if (severity != CompilerFlags.IGNORE) {
HashSet allowedElements = new HashSet();
computeAllowedElements(parentSchema.getType(), allowedElements);
if (!allowedElements.contains(elementName)) {
reportIllegalElement(element, severity);
return;
}
}
}
if (schemaElement == null && parentSchema != null) {
ISchemaAttribute attr = parentSchema.getAttribute(elementName);
if (attr != null && attr.getKind() == IMetaAttribute.JAVA) {
if (attr.isDeprecated())
reportDeprecatedAttribute(element, element.getAttributeNode("class")); //$NON-NLS-1$
validateJavaAttribute(element, element.getAttributeNode("class")); //$NON-NLS-1$
}
} else {
if (schemaElement != null) {
validateRequiredExtensionAttributes(element, schemaElement);
validateExistingExtensionAttributes(element, element.getAttributes(), schemaElement);
if (schemaElement.isDeprecated())
reportDeprecatedElement(element);
if (schemaElement.hasTranslatableContent())
validateTranslatableElementContent(element);
}
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
validateElement((Element)children.item(i), schema);
}
}
}
private void computeAllowedElements(ISchemaType type, HashSet elementSet) {
if (type instanceof ISchemaComplexType) {
ISchemaComplexType complexType = (ISchemaComplexType) type;
ISchemaCompositor compositor = complexType.getCompositor();
if (compositor != null)
computeAllowedElements(compositor, elementSet);
ISchemaAttribute[] attrs = complexType.getAttributes();
for (int i = 0; i < attrs.length; i++) {
if (attrs[i].getKind() == IMetaAttribute.JAVA)
elementSet.add(attrs[i].getName());
}
}
}
private void computeAllowedElements(ISchemaCompositor compositor,
HashSet elementSet) {
ISchemaObject[] children = compositor.getChildren();
for (int i = 0; i < children.length; i++) {
ISchemaObject child = children[i];
if (child instanceof ISchemaObjectReference) {
ISchemaObjectReference ref = (ISchemaObjectReference) child;
ISchemaElement refElement = (ISchemaElement) ref
.getReferencedObject();
if (refElement != null)
elementSet.add(refElement.getName());
} else if (child instanceof ISchemaCompositor) {
computeAllowedElements((ISchemaCompositor) child, elementSet);
}
}
}
private void validateRequiredExtensionAttributes(Element element, ISchemaElement schemaElement) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_NO_REQUIRED_ATT);
if (severity == CompilerFlags.IGNORE)
return;
ISchemaAttribute[] attInfos = schemaElement.getAttributes();
for (int i = 0; i < attInfos.length; i++) {
ISchemaAttribute attInfo = attInfos[i];
if (attInfo.getUse() == ISchemaAttribute.REQUIRED) {
boolean found = element.getAttributeNode(attInfo.getName()) != null;
if (!found && attInfo.getKind() == IMetaAttribute.JAVA) {
NodeList children = element.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
if (attInfo.getName().equals(children.item(j).getNodeName())) {
found = true;
break;
}
}
}
if (!found) {
reportMissingRequiredAttribute(element, attInfo.getName(), severity);
}
}
}
}
private void validateExistingExtensionAttributes(Element element, NamedNodeMap attrs,
ISchemaElement schemaElement) {
for (int i = 0; i < attrs.getLength(); i++) {
Attr attr = (Attr)attrs.item(i);
ISchemaAttribute attInfo = schemaElement.getAttribute(attr.getName());
if (attInfo == null) {
HashSet allowedElements = new HashSet();
computeAllowedElements(schemaElement.getType(), allowedElements);
if (allowedElements.contains(attr.getName())) {
validateJavaAttribute(element, attr);
} else {
int flag = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_ATTRIBUTE);
if (flag != CompilerFlags.IGNORE)
reportUnknownAttribute(element, attr.getName(), flag);
}
} else {
validateExtensionAttribute(element, attr, attInfo);
}
}
}
private void validateExtensionAttribute(Element element, Attr attr, ISchemaAttribute attInfo) {
ISchemaSimpleType type = attInfo.getType();
ISchemaRestriction restriction = type.getRestriction();
if (restriction != null) {
validateRestrictionAttribute(element, attr, restriction);
}
int kind = attInfo.getKind();
if (kind == IMetaAttribute.JAVA) {
validateJavaAttribute(element, attr);
} else if (kind == IMetaAttribute.RESOURCE) {
validateResourceAttribute(element, attr);
} else if (type.getName().equals("boolean")) { //$NON-NLS-1$
validateBoolean(element, attr);
}
validateTranslatableString(element, attr, attInfo.isTranslatable());
if (attInfo.isDeprecated()) {
reportDeprecatedAttribute(element, attr);
}
}
protected void validateExtensionPoint(Element element) {
if (assertAttributeDefined(element, "id", CompilerFlags.ERROR)) { //$NON-NLS-1$
Attr idAttr = element.getAttributeNode("id"); //$NON-NLS-1$
if (!IdUtil.isValidExtensionPointId(idAttr.getValue())) {
String message = NLS.bind(PDEMessages.Builders_Manifest_extensionPointId_value, idAttr.getValue());
report(message, getLine(element, idAttr.getName()),
CompilerFlags.ERROR);
}
}
assertAttributeDefined(element, "name", CompilerFlags.ERROR); //$NON-NLS-1$
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_ATTRIBUTE);
NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Attr attr = (Attr)attrs.item(i);
String name = attr.getName();
if ("name".equals(name)) { //$NON-NLS-1$
validateTranslatableString(element, attr, true);
} else if (!"id".equals(name) && !"schema".equals(name) && severity != CompilerFlags.IGNORE) { //$NON-NLS-1$ //$NON-NLS-2$
reportUnknownAttribute(element, name, severity);
}
}
severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_ELEMENT);
if (severity != CompilerFlags.IGNORE) {
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
reportIllegalElement((Element)children.item(i), severity);
}
}
protected void validateTranslatableString(Element element, Attr attr, boolean shouldTranslate) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_NOT_EXTERNALIZED);
if (severity == CompilerFlags.IGNORE)
return;
String value = attr.getValue();
if (shouldTranslate) {
if (!value.startsWith("%")) { //$NON-NLS-1$
report(NLS.bind(PDEMessages.Builders_Manifest_non_ext_attribute, attr.getName()), getLine(element, attr.getName()), severity); //$NON-NLS-1$
} else if (fModel != null && fModel instanceof AbstractModel) {
NLResourceHelper helper = ((AbstractModel)fModel).getNLResourceHelper();
if (helper == null || !helper.resourceExists(value)) {
report(NLS.bind(PDEMessages.Builders_Manifest_key_not_found, value.substring(1)), getLine(element, attr.getName()), severity); //$NON-NLS-1$
}
}
}
}
protected void validateTranslatableElementContent(Element element) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_NOT_EXTERNALIZED);
if (severity == CompilerFlags.IGNORE)
return;
String value = getTextContent(element);
if (value == null)
return;
if (!value.startsWith("%")) { //$NON-NLS-1$
report(NLS.bind(PDEMessages.Builders_Manifest_non_ext_element, element.getNodeName()), getLine(element), severity); //$NON-NLS-1$
} else if (fModel != null && fModel instanceof AbstractModel) {
NLResourceHelper helper = ((AbstractModel)fModel).getNLResourceHelper();
if (helper == null || !helper.resourceExists(value)) {
report(NLS.bind(PDEMessages.Builders_Manifest_key_not_found, value.substring(1)), getLine(element), severity); //$NON-NLS-1$
}
}
}
protected void validateResourceAttribute(Element element, Attr attr) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_RESOURCE);
if (severity != CompilerFlags.IGNORE && !resourceExists(attr.getValue())) {
report(NLS.bind(PDEMessages.Builders_Manifest_resource, (new String[] { attr.getValue(), attr.getName() })), //$NON-NLS-1$
getLine(element,
attr.getName()),
severity);
}
}
private boolean resourceExists(String location) {
IPath path = new Path(location);
if ("platform:".equals(path.getDevice()) && path.segmentCount() > 2) { //$NON-NLS-1$
if ("plugin".equals(path.segment(0))) { //$NON-NLS-1$
String id = path.segment(1);
IPluginModelBase model = PDECore.getDefault().getModelManager().findModel(id);
if (model != null && model.isEnabled()) {
path = path.setDevice(null).removeFirstSegments(2);
path = new Path(model.getInstallLocation()).append(path);
location = path.toString();
}
}
}
ArrayList paths = new ArrayList();
if (location.indexOf("$nl$") != -1) { //$NON-NLS-1$
StringTokenizer tokenizer = new StringTokenizer(TargetPlatform.getNL(), "_"); //$NON-NLS-1$
String language = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
String country = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
if (language != null && country != null)
paths.add(location
.replaceAll(
"\\$nl\\$", "nl" + IPath.SEPARATOR + language + IPath.SEPARATOR + country)); //$NON-NLS-1$ //$NON-NLS-2$
if (language != null)
paths.add(location.replaceAll(
"\\$nl\\$", "nl" + IPath.SEPARATOR + language)); //$NON-NLS-1$ //$NON-NLS-2$
paths.add(location.replaceAll("\\$nl\\$", "")); //$NON-NLS-1$ //$NON-NLS-2$
} else {
paths.add(location);
}
for (int i = 0; i < paths.size(); i++) {
IPath currPath = new Path(paths.get(i).toString());
if (currPath.isAbsolute() && currPath.toFile().exists())
return true;
if (fFile.getProject().findMember(currPath) != null)
return true;
}
return false;
}
protected void validateJavaAttribute(Element element, Attr attr) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_UNKNOWN_CLASS);
if (severity == CompilerFlags.IGNORE)
return;
String value = attr.getValue();
IJavaProject javaProject = JavaCore.create(fFile.getProject());
try {
// be careful: people have the option to use the format:
// fullqualifiedName:staticMethod
int index = value.indexOf(":"); //$NON-NLS-1$
if (index != -1)
value = value.substring(0, index);
IType javaType = javaProject.findType(value);
if (javaType == null) {
report(NLS.bind(PDEMessages.Builders_Manifest_class, (new String[] { value, attr.getName() })), getLine(
element, attr.getName()), severity);
}
} catch (JavaModelException e) {
}
}
protected void validateRestrictionAttribute(Element element, Attr attr, ISchemaRestriction restriction) {
Object[] children = restriction.getChildren();
String value = attr.getValue();
for (int i = 0; i < children.length; i++) {
Object child = children[i];
if (child instanceof ISchemaEnumeration) {
ISchemaEnumeration enumeration = (ISchemaEnumeration) child;
if (enumeration.getName().equals(value)) {
return;
}
}
}
reportIllegalAttributeValue(element, attr);
}
protected void reportUnusedAttribute(Element element, String attName, int severity) {
String message = NLS.bind(PDEMessages.Builders_Manifest_unused_attribute, attName);
report(message, getLine(element, attName), severity);
}
protected void reportUnusedElement(Element element, int severity) {
Node parent = element.getParentNode();
report(NLS.bind(PDEMessages.Builders_Manifest_unused_element, (new String[] { //$NON-NLS-1$
element.getNodeName(), parent.getNodeName() })),
getLine(element), severity);
}
protected void reportDeprecatedElement(Element element) {
int severity = CompilerFlags.getFlag(fProject, CompilerFlags.P_DEPRECATED);
if (severity != CompilerFlags.IGNORE) {
report(NLS.bind(PDEMessages.Builders_Manifest_deprecated_element, element.getNodeName()), getLine(element), severity);
}
}
}