blob: 4b3d0c842db43f2bae426f222629cc4c8f002daa [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
* David Carver (STAR) - bug 287499 - add XML Catalog Resolution for a file.
* Jesper Steen Moller - bug 304162 - support absolute files properly
*******************************************************************************/
package org.eclipse.wst.xsl.core;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.text.IDocument;
import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.xsl.core.internal.model.StylesheetBuilder;
import org.eclipse.wst.xsl.core.internal.util.FileUtil;
import org.eclipse.wst.xsl.core.model.Stylesheet;
import org.eclipse.wst.xsl.core.model.StylesheetModel;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* The interface to all aspects of the XSL core functionality.
* <p>
* This is responsible for building and maintaining the cache of built XSL
* models.
* </p>
*
* @author Doug Satchwell
*/
public class XSLCore {
/**
* The XSL namespace URI (= http://www.w3.org/1999/XSL/Transform)
*/
public static final String XSL_NAMESPACE_URI = "http://www.w3.org/1999/XSL/Transform"; //$NON-NLS-1$
/**
* The XSL content type (= org.eclipse.wst.xml.core.xslsource)
*/
public static final String XSL_CONTENT_TYPE = "org.eclipse.wst.xml.core.xslsource"; //$NON-NLS-1$
private static XSLCore instance;
private Map<IFile, StylesheetModel> stylesheetsComposed = new HashMap<IFile, StylesheetModel>();
private XSLCore() {
}
/**
* Get the cached stylesheet, or build it if it has not yet been built.
*
* @param file
* @return source file, or null if could not be built
* @since 1.0
*/
public synchronized StylesheetModel getStylesheet(IFile file) {
StylesheetModel stylesheet = stylesheetsComposed.get(file);
if (stylesheet == null)
stylesheet = buildStylesheet(file);
return stylesheet;
}
/**
* Completely rebuild the source file from its DOM
*
* @param file
* @return the stylesheet model, or null if it could not be created.
* @since 1.0
*/
public synchronized StylesheetModel buildStylesheet(IFile file) {
Stylesheet stylesheet = StylesheetBuilder.getInstance().getStylesheet(
file, true);
if (stylesheet == null)
return null;
StylesheetModel stylesheetComposed = new StylesheetModel(stylesheet);
stylesheetsComposed.put(file, stylesheetComposed);
stylesheetComposed.fix();
return stylesheetComposed;
}
/**
* Clean all of the stylesheets from the given project.
*
* @param project
* the project to be cleaned
* @param monitor
* a progress monitor to track the clean progress
*/
public synchronized void clean(IProject project, IProgressMonitor monitor) {
for (Iterator<StylesheetModel> iter = stylesheetsComposed.values()
.iterator(); iter.hasNext();) {
StylesheetModel model = iter.next();
if (project == null
|| project.equals(model.getStylesheet().getFile()
.getProject())) {
iter.remove();
}
}
}
/**
* Get the singleton <code>XSLCore</code> instance.
*
* @return the <code>XSLCore</code> instance
*/
public static synchronized XSLCore getInstance() {
if (instance == null)
instance = new XSLCore();
return instance;
}
/**
* Locates a file for the given current file and URI.
*
* @param currentFile
* the file to resolve relative to
* @param uri
* the relative URI
* @return the file at the URI relative to this <code>currentFile</code>
*/
public static IFile resolveFile(IFile currentFile, String uri) {
if (uri == null || uri.trim().length() == 0)
return null;
IResource resource = currentFile.getParent().findMember(new Path(uri));
if (resource == null) {
String baseURI = currentFile.getRawLocationURI().toString();
String resolvedURI = URIResolverPlugin.createResolver().resolve(baseURI, "", uri); //$NON-NLS-1$
URI parsedUri = null;
boolean isAbsolute = false;
try {
parsedUri = new URI(resolvedURI);
if (parsedUri.isAbsolute()) isAbsolute = true;
} catch (URISyntaxException e) {
}
if (uri != null && isAbsolute) {
if (resolvedURI.startsWith("platform:/resource/")) { //$NON-NLS-1$
String platformPart = resolvedURI.substring(19);
resource = currentFile.getWorkspace().getRoot().findMember(platformPart);
} else {
resource = findBestFileForURI(parsedUri);
}
} else {
resource = currentFile.getParent().findMember(new Path(uri));
}
}
if (resource == null || resource.getType() != IResource.FILE)
return null;
return (IFile) resource;
}
/**
* Find an IFile as close to the root as possible matching this file URI.
*
* @param parsedUri URI from the catalog / resolver lookup
* @return a file resource which exists in the workspace, or null if none were found.
*/
private static IFile findBestFileForURI(URI parsedUri) {
IFile fileNearestRoot = null;
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(parsedUri);
for (int i = 0; i < files.length; i++) {
if (files[i].exists()) {
if (fileNearestRoot == null
||
files[i].getFullPath().segmentCount() < fileNearestRoot.getFullPath().segmentCount()) {
fileNearestRoot = files[i];
}
}
}
return fileNearestRoot;
}
/**
* Determine whether the given file is an XML file by inspecting its content
* types.
*
* @param file
* the file to inspect
* @return true if this file is an XML file
*/
public static boolean isXMLFile(IFile file) {
return FileUtil.isXMLFile(file);
}
/**
* Determine whether the given file is an XSL file by inspecting its content
* types.
*
* @param file
* the file to inspect
* @return true if this file is an XSL file
*/
public static boolean isXSLFile(IFile file) {
return FileUtil.isXSLFile(file);
}
/**
* Takes a given <code>Node</code> and returns whether it is part of the the
* XSLT Namespace.
*
* @param node
* The Node to be checked.
* @return True if part of the XSLT namespace, false otherwise.
* @since 1.0
*/
public static boolean isXSLNamespace(Node node) {
if (hasNamespace(node)) {
return false;
}
return node.getNamespaceURI().equals(XSL_NAMESPACE_URI);
}
/**
* Determine if the Node that was passed has a Namespace. If it doesn't the
* node is either going to be false, or the call to the getNamespace()
* method will return null.
*
* @param node
* @return
*/
private static boolean hasNamespace(Node node) {
return node == null || node.getNamespaceURI() == null;
}
/**
* Returns an Attr node for the current Node if one exits at the specified
* offset.
*
* @param node
* @param offset
* @return A w3c.dom.Attr
* @since 1.0
*/
public static Attr getCurrentAttrNode(Node node, int offset) {
if ((node instanceof IndexedRegion)
&& ((IndexedRegion) node).contains(offset)
&& (node.hasAttributes())) {
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); ++i) {
IndexedRegion attRegion = (IndexedRegion) attrs.item(i);
if (attRegion.contains(offset)) {
return (Attr) attrs.item(i);
}
}
}
return null;
}
/**
* Returns the current Node at the specified offset.
*
* @param document
* @param offset
* @return an w3c.dom.Node
* @since 1.0
*/
public static Node getCurrentNode(IDocument document, int offset) {
IndexedRegion inode = null;
IStructuredModel sModel = null;
try {
sModel = StructuredModelManager.getModelManager()
.getExistingModelForRead(document);
inode = sModel.getIndexedRegion(offset);
if (inode == null)
inode = sModel.getIndexedRegion(offset - 1);
} finally {
if (sModel != null)
sModel.releaseFromRead();
}
if (inode instanceof Node) {
return (Node) inode;
}
return null;
}
}