| /******************************************************************************* |
| * 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); |
| } |
| } |