blob: 7af0e3aba74dc89338d0748e3eff5636faa99838 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2003 GEBIT Gesellschaft fuer EDV-Beratung
* und Informatik-Technologien mbH,
* Berlin, Duesseldorf, Frankfurt (Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation
* IBM Corporation - bug fixes
*******************************************************************************/
package org.eclipse.ant.internal.ui.editor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.text.MessageFormat;
import java.util.Stack;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.util.FileUtils;
import org.eclipse.ant.internal.ui.model.AntUIPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* The <code>DefaultHandler</code> for the parsing of the currently edited file.
*
* @version 19.09.2002
* @author Alf Schiefelbein
*/
public class AntEditorSaxDefaultHandler extends DefaultHandler {
/**
* The locator that tells us the location of the currently parsed element
* in the parsed document.
*/
protected Locator locator;
/**
* Stack of still open elements.
* <P>
* On top of the stack is the innermost element.
*/
protected Stack stillOpenElements = new Stack();
/**
* The parent element that we search for.
* <P>
* This variable will be set during the parsing process, when we first
* passed the cursor location.
*/
protected Element parentElement;
/**
* Flag that determines whether we are finished with parsing.
* <P>
* This is usually the case when the parent element was found and closed.
*/
protected boolean parsingFinished;
/**
* Document that is used to create the elements.
*/
protected Document document;
/**
* The startingRow where the cursor is located in the document.
* <P>
* The first startingRow is refered to with an index of '0'.
*/
protected int rowOfCursorPosition = -1;
/**
* The startingColumn where the cursor is located in the document.
* <P>
* The first startingColumn is refered to with an index of '0'.
*/
protected int columnOfCursorPosition = -1;
/**
* The name of the document root element or null if none seen.
*/
public String rootElementName;
/**
* Used as a helper for resolving external relative entries.
*/
private File mainFileContainer;
/**
* Creates an AntEditorSaxDefaultHandler, with the specified parameters.
*
* @param aRowOfCursorPosition the startingRow where the cursor is located in the
* document. The first startingRow is refered to with an index of '0'.
* @param aColumnOfCursorPosition the startingColumn where the cursor is located in
* the document. The first startingColumn is refered to with an index of '0'.
*/
public AntEditorSaxDefaultHandler(File fileContainer, int aRowOfCursorPosition, int aColumnOfCursorPosition) throws ParserConfigurationException {
super();
if (AntUIPlugin.getDefault() != null && AntUIPlugin.getDefault().isDebugging()) {
AntUIPlugin.log("AntEditorSaxDefaultHandler(" +aRowOfCursorPosition+ ", "+aColumnOfCursorPosition+ ")", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
if (aRowOfCursorPosition < 0 || aColumnOfCursorPosition < 0) {
throw new IllegalArgumentException(MessageFormat.format(AntEditorMessages.getString("AntEditorSaxDefaultHandler.Invalid_cursor_position"), new String[]{Integer.toString(aRowOfCursorPosition), Integer.toString(aColumnOfCursorPosition)})); //$NON-NLS-1$
}
rowOfCursorPosition = aRowOfCursorPosition;
columnOfCursorPosition = aColumnOfCursorPosition;
this.mainFileContainer= fileContainer;
initialize();
}
/**
* Initializes the handler.
*/
protected void initialize() throws ParserConfigurationException {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
document = documentBuilder.newDocument();
}
/**
* Checks whether the parent element, that we are searching for can be or
* has already been determined.
* <P>
* This will be done by comparing the current parsing position with the
* cursor position. If we just passed the cursor position and the parent
* element has not been set yet, it will be set.
*
* @return <code>true</code> if the parent element is known otherwise
* <code>false</code>
*/
protected boolean checkForParentElement() {
if(parentElement == null) {
if(locator != null) {
/*
* The locator's numbers are 1-based though, we do everything
* 0-based.
*/
int lineNum = locator.getLineNumber() -1;
int columnNum = locator.getColumnNumber() -1;
if(lineNum> rowOfCursorPosition ||
(lineNum == rowOfCursorPosition && columnNum > columnOfCursorPosition) && !stillOpenElements.isEmpty()) {
parentElement = (Element)stillOpenElements.peek();
if (AntUIPlugin.getDefault() != null && AntUIPlugin.getDefault().isDebugging()) {
AntUIPlugin.log("AntEditorSaxDefaultHandler.checkForParentElement(): Parent element found: " +parentElement, null); //$NON-NLS-1$
}
return true;
}
}
return false;
}
// Parent element has been set already before
return true;
}
/* (non-Javadoc)
* @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
*/
public void startElement(String uri, String localName, String qualifiedName, Attributes attributes)
throws SAXException {
/*
* While the crimson parser passes the tag name as local name, apache's
* xerces parser, passes the tag name as qualilfied name and an empty
* string as local name.
*/
if (AntUIPlugin.getDefault() != null && AntUIPlugin.getDefault().isDebugging()) {
AntUIPlugin.log("AntEditorSaxDefaultHandler.startElement(" +uri+ ", " +localName+ ", "+qualifiedName+ ", "+attributes+ ")", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
if(parsingFinished) {
return;
}
// Checks whether we know the parent for sure
checkForParentElement();
// Create a Dom Element
String tagName = localName.length() > 0 ? localName : qualifiedName;
if(tagName == null || tagName.length() == 0) {
throw new AntEditorException(AntEditorMessages.getString("AntEditorSaxDefaultHandler.Error_parsing")); //$NON-NLS-1$
}
// This code added to determine root element in a rational way bf
if (rootElementName == null) {
rootElementName = tagName;
}
Element element = document.createElement(tagName);
stillOpenElements.push(element);
super.startElement(uri, localName, qualifiedName, attributes);
}
/* (non-Javadoc)
* @see org.xml.sax.ContentHandler#endElement(String, String, String)
*/
public void endElement(String aUri, String aLocalName, String aQualifiedName)
throws SAXException {
if (AntUIPlugin.getDefault() != null && AntUIPlugin.getDefault().isDebugging()) {
AntUIPlugin.log("AntEditorSaxDefaultHandler.endElement(" +aUri+ ", " +aLocalName+ ", "+aQualifiedName+ ")", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
if(parsingFinished) {
return;
}
// Checks whether we know the parent for sure
boolean parentKnown = checkForParentElement();
String tagName = aLocalName.length() > 0 ? aLocalName : aQualifiedName;
if(!stillOpenElements.isEmpty()) {
Element lastStillOpenElement = (Element)stillOpenElements.peek();
if(lastStillOpenElement != null && lastStillOpenElement.getTagName().equals(tagName)) {
stillOpenElements.pop();
if(!stillOpenElements.empty()) {
Element secondLastStillOpenElement = (Element)stillOpenElements.peek();
secondLastStillOpenElement.appendChild(lastStillOpenElement);
}
if(parentKnown && parentElement != null && parentElement.getTagName().equals(tagName)) {
parsingFinished = true;
}
}
}
}
/* (non-Javadoc)
* @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
*/
public void setDocumentLocator(Locator aLocator) {
locator = aLocator;
super.setDocumentLocator(aLocator);
}
/**
* Returns the parent element that has been determined during a prior
* parsing.
* <P>
* It is quite common that parsing stopped before the current cursor
* position. That happens when the parser finds an error within the parsed
* document before. In that case the parent element might be guessed to be
* the one that opened last. To tell the handler whether the parent should
* be guessed, <code>guessParent</code> may be specified.
*
* @param aGuessParentFlag whether the parent should be guessed
* @return the parent element or <code>null</code> if not known.
*/
public Element getParentElement(boolean guessParent) {
if(parentElement != null) {
return parentElement;
}
if(guessParent) {
if(!stillOpenElements.empty()) {
return (Element)stillOpenElements.peek();
}
}
return null;
}
/**
* We have to handle fatal errors.
* <P>
* They come up whenever we parse a not valid file, what we do all the time.
* Therefore a fatal error is nothing special for us.
* <P>
* Actually, we ignore all fatal errors for now.
*
* @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
*/
public void fatalError(SAXParseException anException) throws SAXException {
if(locator != null) {
// int tempLineNr = locator.getLineNumber() -1;
// int tempColumnNr = locator.getColumnNumber() -1;
// super.fatalError(anException);
}
}
/**
* @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
*/
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
int index= systemId.indexOf(':');
if (index > 0) {
//remove file:
systemId= systemId.substring(index+1, systemId.length());
}
File resolvedFile= null;
IPath filePath= new Path(systemId);
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(filePath);
if (file == null || !file.exists()) {
//relative path
try {
//this call is ok if mainFileContainer is null
resolvedFile= FileUtils.newFileUtils().resolveFile(mainFileContainer, systemId);
} catch (BuildException be) {
return null;
}
} else {
resolvedFile= file.getLocation().toFile();
}
if (resolvedFile != null && resolvedFile.exists()) {
try {
return new InputSource(new FileReader(resolvedFile));
} catch (FileNotFoundException e) {
return null;
}
}
return super.resolveEntity(publicId, systemId);
}
}