/*******************************************************************************
* This file is part of the Symfony eclipse plugin.
*
* (c) Robert Gruendler <r.gruendler@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
******************************************************************************/
package com.dubture.symfony.core.parser;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.dubture.symfony.core.log.Logger;
import com.dubture.symfony.index.model.Route;
import com.dubture.symfony.index.model.Service;
/**
*
* {@link XMLConfigParser} retrieves project configuration
* from xml files and contributes it to the Symfony2 model.
*
*
* @author Robert Gruendler <r.gruendler@gmail.com>
*
*/
public class XMLConfigParser implements IConfigParser {
private XPath xPath;
private Document doc;
private HashMap<String, String> parameters;
private HashMap<String, Service> services;
private Stack<Route> routes = new Stack<Route>();
public XMLConfigParser(InputStream inputStream) throws Exception {
doc = PositionalXMLReader.readXML(inputStream);
xPath = XPathFactory.newInstance().newXPath();
parameters = new HashMap<String, String>();
services = new HashMap<String, Service>();
}
@Override
public void parse() throws Exception {
// get parameters
parseParameters();
// get services
parseServices();
// get aliased services
parseAliases();
// parse synthetic services
parseSynthetic();
// parse routes
parseRoutes();
}
private void parseRoutes() {
try {
String servicePath = "/routes/route";
NodeList routeNodes = getNodes(servicePath);
for (int i = 0; i < routeNodes.getLength(); i++) {
Node node = routeNodes.item(i);
NamedNodeMap atts = node.getAttributes();
String name = null;
String pattern = null;
for (int j = 0; j < atts.getLength(); j++) {
Attr attr = (Attr) atts.item(j);
String key = attr.getName();
if (key.equals("id"))
name = attr.getValue();
else if (key.equals("pattern"))
pattern = attr.getValue();
}
XPathExpression expr = xPath.compile("default[@key='_controller']");
Object _default = expr.evaluate(node, XPathConstants.NODESET);
NodeList defaults = (NodeList) _default;
if (defaults.getLength() == 1) {
Node controllerNode = defaults.item(0);
routes.push(new Route(name, pattern, controllerNode.getTextContent()));
}
}
} catch (Exception e) {
Logger.logException(e);
}
}
/**
* Parse the service parameters from the XML file.
*
*
* @throws Exception
*/
private void parseParameters() throws Exception {
String expr = "/container/parameters/parameter[@key]";
XPathExpression xpathExpr = xPath.compile(expr);
Object result = xpathExpr.evaluate(doc,XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
NamedNodeMap atts = node.getAttributes();
for (int j = 0; j < atts.getLength(); j++) {
Attr attr = (Attr) atts.item(j);
if (attr != null && attr.getName() != null && attr.getName().equals("key")) {
parameters.put(attr.getValue(), node.getTextContent());
break;
}
}
}
}
/**
* Parse services if available from the xml file.
*
* @throws Exception
*/
@SuppressWarnings("rawtypes")
private void parseServices() throws Exception {
NodeList nodeList = doc.getElementsByTagName("service");
for (int i=0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
NamedNodeMap attributes = childNode.getAttributes();
Node idItem = attributes.getNamedItem("id");
if (idItem == null) {
continue;
}
String id = idItem.getNodeValue();
String phpClass = null;
Node namedItem = attributes.getNamedItem("class");
if (namedItem != null) {
phpClass = namedItem.getNodeValue();
} else {
Logger.log(Logger.INFO, "Unable to parse class attribute from service " + childNode);
}
Node publicItem = attributes.getNamedItem("public");
String _public = null;
if (publicItem != null) {
_public = attributes.getNamedItem("public").getNodeValue();
}
if (phpClass != null && id != null) {
Service _service = null;
if (phpClass.startsWith("%") && phpClass.endsWith("%")) {
String placeHolder = phpClass.replace("%", "");
Iterator it = getParameters().keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
String val = (String) getParameters().get(key);
if (placeHolder.equals(key)) {
_service = new Service(id, val, null);
}
}
} else {
_service = new Service(id, phpClass, null);
}
NodeList children = childNode.getChildNodes();
List<Node> tags = new ArrayList<Node>();
List<Node> arguments = new ArrayList<Node>();
List<Node> calls = new ArrayList<Node>();
for (int j=0; j < children.getLength(); j++) {
Node child = children.item(j);
if ("tag".equals(child.getNodeName())) {
tags.add(child);
} else if ("argument".equals(child.getNodeName())) {
arguments.add(child);
} else if ("call".equals(child.getNodeName())) {
calls.add(child);
}
}
if (_service != null) {
_service.setPublic(_public);
_service.setLine(Integer.parseInt((String) childNode.getUserData("lineNumber")));
for(Node tag : tags) {
NamedNodeMap map = tag.getAttributes();
Node tagName = map.getNamedItem("name");
if (tagName != null && tagName.getNodeValue() != null) {
_service.addTag(tagName.getNodeValue());
}
}
}
synchronized (services) {
services.put(id, _service);
}
}
}
/*
String servicePath = "/container/services/service[@class]";
NodeList serviceNodes = getNodes(servicePath);
for (int i = 0; i < serviceNodes.getLength(); i++) {
Element service = (Element) serviceNodes.item(i);
//TODO: Check the services visibility and if it's abstract
Object userData = service.getUserData(LocationRecordingHandler.KEY_LINE_NO);
System.err.println(userData);
String id = service.getAttribute("id");
String phpClass = service.getAttribute("class");
String _public = service.getAttribute("public");
NodeList tags = service.getElementsByTagName("tag");
if (_public == null || _public.equals(""))
_public = "true";
if (phpClass != null && id != null) {
Service _service = null;
if (phpClass.startsWith("%") && phpClass.endsWith("%")) {
String placeHolder = phpClass.replace("%", "");
Iterator it = getParameters().keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
String val = (String) getParameters().get(key);
if (placeHolder.equals(key)) {
_service = new Service(id, val, null);
}
}
} else {
_service = new Service(id, phpClass, null);
}
for(int k=0; k < tags.getLength(); k++) {
Node tag = tags.item(k);
NamedNodeMap map = tag.getAttributes();
Node tagName = map.getNamedItem("name");
if (tagName != null && tagName.getNodeValue() != null)
_service.addTag(tagName.getNodeValue());
}
if (_service != null)
_service.setPublic(_public);
synchronized (services) {
services.put(id, _service);
}
}
}
*/
}
@SuppressWarnings("rawtypes")
private void parseAliases() throws Exception {
String servicePath = "/container/services/service[@alias]";
NodeList serviceNodes = getNodes(servicePath);
for (int i = 0; i < serviceNodes.getLength(); i++) {
Element service = (Element) serviceNodes.item(i);
String id = service.getAttribute("id");
String alias = service.getAttribute("alias");
if (alias != null && id != null) {
synchronized (services) {
Iterator it = services.keySet().iterator();
HashMap<String, Service> newServices = new HashMap<String, Service>();
while (it.hasNext()) {
String aliasID = (String) it.next();
Service _s= services.get(aliasID);
if (_s != null) {
_s.addAlias(aliasID);
if (alias.equals(aliasID)) {
newServices.put(id, _s);
}
}
}
services.putAll(newServices);
}
}
}
}
private void parseSynthetic() throws Exception {
String servicePath = "/container/services/service[@synthetic]";
NodeList serviceNodes = getNodes(servicePath);
for (int i = 0; i < serviceNodes.getLength(); i++) {
Element service = (Element) serviceNodes.item(i);
//TODO: Check the services visibility and if it's abstract
String id = service.getAttribute("id");
String isSynthetic = service.getAttribute("synthetic");
if (isSynthetic != null && id != null) {
services.put(id, new Service(id, "synthetic", null));
}
}
}
/**
* Retrieve a list of nodes for a given xpath expression
*
* @param path
* @return
* @throws Exception
*/
private NodeList getNodes(String path) throws Exception {
XPathExpression xpathExpr = xPath.compile(path);
Object result = xpathExpr.evaluate(doc,XPathConstants.NODESET);
return (NodeList) result;
}
/**
* Get all loades services.
*
*
* @return
*/
public HashMap<String, Service> getServices() {
return services;
}
/**
* Did the parser find any services definitions?
*
* @return
*/
public boolean hasServices() {
return services.size() > 0;
}
public HashMap<String, String> getParameters() {
return parameters;
}
public boolean hasRoutes() {
return routes.size() > 0;
}
public Stack<Route> getRoutes() {
return routes;
}
}