/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.core.internal.classpath;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
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.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* @author Andrew Eisenberg
* @author Nieraj Singh
* @created Jan 28, 2010
*/
@SuppressWarnings("nls")
public class PluginDescriptorParser implements IPluginParser {
private static final String PROPERTY = "property";
private static final String ARGUMENT = "argument";
private static final String METHOD = "method";
private static final String TYPE = "type";
private static final String CONTROLLER = "controller";
private static final String DOMAIN = "domain";
private static final String ARTEFACT = "artefact";
private static final String BEHAVIOR = "behavior";
private static final String DESCRIPTION = "description";
private static final String TITLE = "title";
private static final String AUTHOR = "author";
private static final String GRAILS_VERSION = "grailsVersion";
private static final String VERSION = "version";
private static final String NAME = "name";
private static final String PLUGIN = "plugin";
private static final String BEAHVIOR_TYPE_NAME = "org.grails.Behavior";
private final String pluginFileLocation;
public PluginDescriptorParser(String pluginFileLocation) {
this.pluginFileLocation = pluginFileLocation;
}
public GrailsPluginVersion parse() {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
.newInstance();
GrailsPluginVersion data = new GrailsPluginVersion();
try {
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new File(pluginFileLocation)
.toURI().toString());
NodeList nodes = doc.getElementsByTagName(PLUGIN);
if (nodes != null && nodes.getLength() > 0) {
// should have exactly one element
Node pluginNode = nodes.item(0);
NamedNodeMap attrMap = pluginNode.getAttributes();
if (attrMap != null) {
Node attrNode = attrMap.getNamedItem(NAME);
if (attrNode != null) {
data.setName(attrNode.getTextContent());
}
attrNode = attrMap.getNamedItem(VERSION);
if (attrNode != null) {
data.setVersion(attrNode.getTextContent());
}
attrNode = attrMap.getNamedItem(GRAILS_VERSION);
if (attrNode != null) {
data.setRuntimeVersion(attrNode.getTextContent());
}
}
NodeList childNodes = pluginNode.getChildNodes();
if (childNodes != null) {
for (int i = 0, length = childNodes.getLength(); i < length; i++) {
Node childNode = childNodes.item(i);
String nodeName = childNode.getNodeName();
if (nodeName.equals(AUTHOR)) {
data.setAuthor(handleSimpleNode(childNode));
} else if (nodeName.equals(TITLE)) {
data.setTitle(handleSimpleNode(childNode));
} else if (nodeName.equals(DESCRIPTION)) {
data.setDescription(handleSimpleNode(childNode));
} else if (nodeName.equals(BEHAVIOR)) {
handleBehavior(childNode, data);
}
}
}
}
} catch (FileNotFoundException e) {
GrailsCoreActivator.log(e);
} catch (ParserConfigurationException e) {
GrailsCoreActivator.log(e);
} catch (SAXException e) {
GrailsCoreActivator.log(e);
} catch (IOException e) {
GrailsCoreActivator.log(e);
}
return data;
}
private void handleBehavior(Node behaviorNodeParent, GrailsPluginVersion data) {
NodeList behaviorNodes = behaviorNodeParent.getChildNodes();
if (behaviorNodes == null) {
return;
}
for (int i = 0, length = behaviorNodes.getLength(); i < length; i++) {
Node behaviorNode = behaviorNodes.item(i);
NamedNodeMap attrs = behaviorNode.getAttributes();
if (attrs == null) {
continue;
}
Node attr = attrs.getNamedItem(ARTEFACT);
String artefact;
if (attr != null) {
artefact = attr.getTextContent();
} else {
// malformed
continue;
}
// any other artefacts we are interested in???
boolean isDomain = artefact.toLowerCase().equals(DOMAIN);
boolean isController = artefact.toLowerCase().equals(CONTROLLER);
if (!(isDomain || isController)) {
continue;
}
attr = attrs.getNamedItem(NAME);
String name;
if (attr != null) {
name = attr.getTextContent();
} else {
// malformed
continue;
}
attr = attrs.getNamedItem(TYPE);
String typeName;
if (attr != null) {
typeName = attr.getTextContent();
} else {
// malformed
continue;
}
ClassNode type = getClassForType(typeName);
if (behaviorNode.getNodeName().equals(METHOD)) {
List<ClassNode> paramTypes = new ArrayList<ClassNode>();
// get arguments
NodeList args = behaviorNode.getChildNodes();
for (int iArgs = 0, lengthArgs = args.getLength(); iArgs < lengthArgs; iArgs++) {
Node arg = args.item(iArgs);
if (arg.getNodeName().equals(ARGUMENT)) {
NamedNodeMap typeAttr = arg.getAttributes();
if (typeAttr != null) {
Node argType = typeAttr.getNamedItem(TYPE);
if (argType != null) {
ClassNode argClass = getClassForType(argType
.getTextContent());
paramTypes.add(argClass);
}
}
}
}
ClassNode[] paramTypesArr = paramTypes
.toArray(new ClassNode[paramTypes.size()]);
if (isDomain) {
data.addDomainMethod(name, type, paramTypesArr);
} else if (isController) {
data.addControllerMethod(name, type, paramTypesArr);
}
} else if (behaviorNode.getNodeName().equals(PROPERTY)) {
if (isDomain) {
data.addDomainProperty(name, type);
} else if (isController) {
data.addControllerProperty(name, type);
}
}
}
}
/**
* We don't want to use a ClassHelper. should use PerProjectTypeCache
*/
private ClassNode getClassForType(String type) {
if (type.equals(BEAHVIOR_TYPE_NAME)) {
return null; // filled in later
}
return ClassHelper.make(type);
}
private String handleSimpleNode(Node node) {
return node.getFirstChild().getTextContent();
}
}