blob: 768c3e2982df6ecbabc2e8ae43e8497fbfa4062c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Chase Technology Ltd - http://www.chasetechnology.co.uk
* 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:
* Doug Satchwell (Chase Technology Ltd) - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.xsl.core.internal.model;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xsl.core.XSLCore;
import org.eclipse.wst.xsl.core.internal.XSLCorePlugin;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* A builder that creates and maintains a cache of <code>Stylesheet</code>'s.
*
* @author Doug Satchwell
*/
public class StylesheetBuilder
{
private static StylesheetBuilder instance;
private final Map<IFile, Stylesheet> builtFiles = new HashMap<IFile, Stylesheet>();
private StylesheetBuilder()
{
}
/**
* Get the <code>Stylesheet</code> associated with the given file.
* If either the <code>Stylesheet</code> has not yet been created or <code>force</code> is
* specified then the <code>Stylesheet</code> is built.
*
* @param file the XSL file
* @param force <code>true</code> to force a parse of the file
* @return the <code>Stylesheet</code>
*/
public Stylesheet getStylesheet(IFile file, boolean force)
{
Stylesheet stylesheet = builtFiles.get(file);
if (stylesheet == null || force)
{
stylesheet = build(file);
builtFiles.put(file, stylesheet);
}
return stylesheet;
}
private Stylesheet build(IFile file)
{
// System.out.println("Building "+file+"...");
// long start = System.currentTimeMillis();
Stylesheet stylesheet = null;
IStructuredModel smodel = null;
try
{
smodel = StructuredModelManager.getModelManager().getExistingModelForRead(file);
if (smodel == null)
{
smodel = StructuredModelManager.getModelManager().getModelForRead(file);
// long endParse = System.currentTimeMillis();
// System.out.println("PARSE " + file + " in " + (endParse - start) + "ms");
}
else
{
// long endParse = System.currentTimeMillis();
// System.out.println("NO-PARSE " + file + " in " + (endParse - start) + "ms");
}
// start = System.currentTimeMillis();
if (smodel != null && smodel instanceof IDOMModel)
{
IDOMModel model = (IDOMModel) smodel;
stylesheet = parseModel(model, file);
}
}
catch (IOException e)
{
XSLCorePlugin.log(e);
}
catch (CoreException e)
{
XSLCorePlugin.log(e);
}
finally
{
if (smodel != null)
smodel.releaseFromRead();
}
// long end = System.currentTimeMillis();
// System.out.println("BUILD " + file + " in " + (end - start) + "ms");
return stylesheet;
}
private Stylesheet parseModel(IDOMModel model, IFile file)
{
IDOMDocument document = model.getDocument();
Stylesheet sf = new Stylesheet(file);
StylesheetParser walker = new StylesheetParser(sf);
walker.walkDocument(document);
return sf;
}
/**
* Get the singleton <code>StylesheetBuilder</code> instance.
*
* @return the <code>StylesheetBuilder</code> instance
*/
public static synchronized StylesheetBuilder getInstance()
{
if (instance == null)
{
instance = new StylesheetBuilder();
}
return instance;
}
private static class StylesheetParser
{
private final Stylesheet sf;
private final Stack<Element> elementStack = new Stack<Element>();
private Template currentTemplate;
private CallTemplate currentCallTemplate;
private XSLElement parentEl;
public StylesheetParser(Stylesheet stylesheet)
{
this.sf = stylesheet;
}
public void walkDocument(IDOMDocument document)
{
if (document.getDocumentElement() != null)
recurse(document.getDocumentElement());
}
private void recurse(Element element)
{
if (XSLCore.XSL_NAMESPACE_URI.equals(element.getNamespaceURI()))
{
XSLElement xslEl;
String elName = element.getLocalName();
if ("stylesheet".equals(elName) && elementStack.size() == 0) //$NON-NLS-1$
{
xslEl = sf;
}
else if ("include".equals(elName) && elementStack.size() == 1) //$NON-NLS-1$
{
Include include = new Include(sf);
sf.addInclude(include);
xslEl = include;
}
else if ("import".equals(elName) && elementStack.size() == 1) //$NON-NLS-1$
{
Import include = new Import(sf);
sf.addImport(include);
xslEl = include;
}
else if ("template".equals(elName) && elementStack.size() == 1) //$NON-NLS-1$
{
currentTemplate = new Template(sf);
sf.addTemplate(currentTemplate);
xslEl = currentTemplate;
}
else if ("param".equals(elName) && elementStack.size() == 2 && currentTemplate != null) //$NON-NLS-1$
{
Parameter param = new Parameter(sf);
// determine whether param has a value
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++)
{
Node childNode = childNodes.item(i);
if (childNode.getNodeType() != Node.ATTRIBUTE_NODE)
{
param.setValue(true);
break;
}
}
currentTemplate.addParameter(param);
xslEl = param;
}
else if ("call-template".equals(elName) && elementStack.size() >= 2) //$NON-NLS-1$
{
currentCallTemplate = new CallTemplate(sf);
sf.addCalledTemplate(currentCallTemplate);
xslEl = currentCallTemplate;
}
else if ("with-param".equals(elName) && elementStack.size() >= 3 && currentCallTemplate != null) //$NON-NLS-1$
{
Parameter param = new Parameter(sf);
// determine whether param has a value
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++)
{
Node childNode = childNodes.item(i);
if (childNode.getNodeType() != Node.ATTRIBUTE_NODE)
{
param.setValue(true);
break;
}
}
currentCallTemplate.addParameter(param);
xslEl = param;
}
else if ("variable".equals(elName) && elementStack.size() == 1) //$NON-NLS-1$
{
Variable var = new Variable(sf);
sf.addGlobalVariable(var);
xslEl = var;
}
else
{
xslEl = new XSLElement(sf);
}
configure((IDOMNode) element, xslEl);
}
elementStack.push(element);
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++)
{
Node node = childNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE)
{
recurse((Element) node);
}
}
elementStack.pop();
// currentTemplate = null;
// currentCallTemplate = null;
}
private void configure(IDOMNode node, XSLElement element)
{
setPositionInfo(node, element);
NamedNodeMap map = node.getAttributes();
for (int i = 0; i < map.getLength(); i++)
{
IDOMAttr attr = (IDOMAttr) map.item(i);
XSLAttribute xslatt = new XSLAttribute(element, attr.getName(), attr.getValue());
setPositionInfo(attr, xslatt);
element.setAttribute(xslatt);
}
if (parentEl != null)
parentEl.addChild(element);
parentEl = element;
}
private static void setPositionInfo(IDOMNode node, XSLNode inc)
{
try
{
IStructuredDocument structuredDocument = node.getStructuredDocument();
int line = structuredDocument.getLineOfOffset(node.getStartOffset());
int lineOffset = structuredDocument.getLineOffset(line);
int col = node.getStartOffset() - lineOffset;
inc.setLineNumber(line);
inc.setColumnNumber(col);
inc.setLength(node.getLength());
}
catch (BadLocationException e)
{
XSLCorePlugin.log(e);
}
}
}
}