/******************************************************************************* * Copyright (c) 2009, 2016 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.documentModel.parser; import java.io.Reader; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.dltk.annotations.Nullable; import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTokenizer; import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; import org.eclipse.wst.sse.core.internal.util.Debug; import org.eclipse.wst.xml.core.internal.parser.XMLSourceParser; import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; public class PHPSourceParser extends XMLSourceParser { public static ThreadLocal<IResource> editFile = new ThreadLocal<IResource>(); private IProject project = null; public PHPSourceParser() { super(); IResource resource = (IResource) editFile.get(); if (resource instanceof IProject) { project = (IProject) resource; } else if (resource instanceof IFile) { project = ((IFile) resource).getProject(); } } /* * Change the Tokenizer used by the XMLSourceParser to make it PHP aware */ public BlockTokenizer getTokenizer() { if (fTokenizer == null) { PHPTokenizer phpTokenizer = new PHPTokenizer(); phpTokenizer.setProject(project); fTokenizer = phpTokenizer; } return fTokenizer; } public RegionParser newInstance() { PHPSourceParser newInstance = new PHPSourceParser(); PHPTokenizer tokenizer = (PHPTokenizer) getTokenizer().newInstance(); tokenizer.setProject(project); newInstance.setTokenizer(tokenizer); return newInstance; } private IStructuredDocumentRegion headNode = null; private IStructuredDocumentRegion lastNode = null; private IStructuredDocumentRegion currentNode = null; protected IStructuredDocumentRegion parseNodes() { // regions are initially reported as complete offsets within the // scanned input // they are adjusted here to be indexes from the currentNode's start // offset // reset the state headNode = lastNode = currentNode = null; ITextRegion region = null; String type = null; while ((region = getNextRegion()) != null) { type = region.getType(); // these types (might) demand a IStructuredDocumentRegion for each // of them if (type == DOMRegionContext.BLOCK_TEXT) { if (currentNode != null && currentNode.getLastRegion().getType() == DOMRegionContext.BLOCK_TEXT) { // multiple block texts indicated embedded containers; no // new IStructuredDocumentRegion currentNode.addRegion(region); currentNode.setLength(region.getEnd() - currentNode.getStart()); region.adjustStart(-currentNode.getStart()); // DW 4/16/2003 regions no longer have parents // region.setParent(currentNode); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } } else { // not continuing a IStructuredDocumentRegion if (currentNode != null) { // ensure that any existing node is at least // terminated if (!currentNode.isEnded()) { currentNode.setLength(region.getStart() - currentNode.getStart()); // fCurrentNode.setTextLength(region.getStart() - // fCurrentNode.getStart()); } lastNode = currentNode; } fireNodeParsed(currentNode); currentNode = createStructuredDocumentRegion(type); if (lastNode != null) { lastNode.setNext(currentNode); } currentNode.setPrevious(lastNode); currentNode.setStart(region.getStart()); currentNode.setLength(region.getLength()); // currentNode.setLength(region.getEnd() // - currentNode.getStart()); currentNode.setEnded(true); region.adjustStart(-currentNode.getStart()); currentNode.addRegion(region); // DW 4/16/2003 regions no longer have parents // region.setParent(currentNode); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } } } // the following contexts OPEN new StructuredDocumentRegions else if ((currentNode != null && currentNode.isEnded()) || (type == PHPRegionContext.PHP_OPEN) || (type == DOMRegionContext.XML_CONTENT) || (type == DOMRegionContext.XML_CHAR_REFERENCE) || (type == DOMRegionContext.XML_ENTITY_REFERENCE) || (type == DOMRegionContext.XML_TAG_OPEN) || (type == DOMRegionContext.XML_END_TAG_OPEN) || (type == DOMRegionContext.XML_COMMENT_OPEN) || (type == DOMRegionContext.XML_CDATA_OPEN) || (type == DOMRegionContext.XML_DECLARATION_OPEN)) { if (currentNode != null) { // ensure that any existing node is at least terminated if (!currentNode.isEnded()) { currentNode.setLength(region.getStart() - currentNode.getStart()); // fCurrentNode.setTextLength(region.getStart() - // fCurrentNode.getStart()); } lastNode = currentNode; } fireNodeParsed(currentNode); currentNode = createStructuredDocumentRegion(type); if (lastNode != null) { lastNode.setNext(currentNode); } currentNode.setPrevious(lastNode); currentNode.setStart(region.getStart()); currentNode.addRegion(region); currentNode.setLength(region.getEnd() - currentNode.getStart()); region.adjustStart(-currentNode.getStart()); // DW 4/16/2003 regions no longer have parents // region.setParent(currentNode); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } } // the following contexts neither open nor close // StructuredDocumentRegions; just add to them else if ((type == DOMRegionContext.XML_TAG_NAME) || (type == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) || (type == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) || (type == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) || (type == DOMRegionContext.XML_COMMENT_TEXT) || (type == DOMRegionContext.XML_PI_CONTENT) || (type == DOMRegionContext.XML_DOCTYPE_INTERNAL_SUBSET) || (type == PHPRegionContext.PHP_CONTENT)) { currentNode.addRegion(region); currentNode.setLength(region.getEnd() - currentNode.getStart()); region.adjustStart(-currentNode.getStart()); // DW 4/16/2003 regions no longer have parents // region.setParent(currentNode); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } } // the following contexts close off StructuredDocumentRegions // cleanly else if ((type == PHPRegionContext.PHP_CLOSE) || (type == DOMRegionContext.XML_PI_CLOSE) || (type == DOMRegionContext.XML_TAG_CLOSE) || (type == DOMRegionContext.XML_EMPTY_TAG_CLOSE) || (type == DOMRegionContext.XML_COMMENT_CLOSE) || (type == DOMRegionContext.XML_DECLARATION_CLOSE) || (type == DOMRegionContext.XML_CDATA_CLOSE)) { currentNode.setEnded(true); currentNode.setLength(region.getEnd() - currentNode.getStart()); currentNode.addRegion(region); region.adjustStart(-currentNode.getStart()); // DW 4/16/2003 regions no longer have parents // region.setParent(currentNode); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } } // this is extremely rare, but valid else if (type == DOMRegionContext.WHITE_SPACE) { ITextRegion lastRegion = currentNode.getLastRegion(); // pack the embedded container with this region if (lastRegion instanceof ITextRegionContainer) { ITextRegionContainer container = (ITextRegionContainer) lastRegion; container.getRegions().add(region); // containers must have parent set ... // setting for EACH subregion is redundent, but not sure // where else to do, so will do here for now. container.setParent(currentNode); // DW 4/16/2003 regions no longer have parents // region.setParent(container); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } region.adjustStart(container.getLength() - region.getStart()); } currentNode.getLastRegion().adjustLength(region.getLength()); currentNode.adjustLength(region.getLength()); } else if (type == DOMRegionContext.UNDEFINED && currentNode != null) { // skip on a very-first region situation as the default // behavior is good enough // combine with previous if also undefined if (currentNode.getLastRegion() != null && currentNode.getLastRegion().getType() == DOMRegionContext.UNDEFINED) { currentNode.getLastRegion().adjustLength(region.getLength()); currentNode.adjustLength(region.getLength()); } // previous wasn't undefined else { currentNode.addRegion(region); currentNode.setLength(region.getEnd() - currentNode.getStart()); region.adjustStart(-currentNode.getStart()); } } else { // if an unknown type is the first region in the document, // ensure that a node exists if (currentNode == null) { currentNode = createStructuredDocumentRegion(type); currentNode.setStart(region.getStart()); } currentNode.addRegion(region); currentNode.setLength(region.getEnd() - currentNode.getStart()); region.adjustStart(-currentNode.getStart()); // DW 4/16/2003 regions no longer have parents // region.setParent(currentNode); if (region instanceof ITextRegionContainer) { ((ITextRegionContainer) region).setParent(currentNode); } if (Debug.debugTokenizer) System.out.println(getClass().getName() + " found region of not specifically handled type " //$NON-NLS-1$ + region.getType() + " @ " + region.getStart() + "[" + region.getLength() + "]"); //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ // $NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ } // these regions also get their own node, so close them cleanly // NOTE: these regions have new StructuredDocumentRegions created // for them above; it may // be more readable if that is handled here as well, but the // current layout // ensures that they open StructuredDocumentRegions the same way if ((type == DOMRegionContext.XML_CONTENT) || (type == DOMRegionContext.XML_CHAR_REFERENCE) || (type == DOMRegionContext.XML_ENTITY_REFERENCE) || (type == PHPRegionContext.PHP_CLOSE)) { currentNode.setEnded(true); } if (headNode == null && currentNode != null) { headNode = currentNode; } } if (currentNode != null) { fireNodeParsed(currentNode); currentNode.setPrevious(lastNode); } // fStringInput = null; primReset(); return headNode; } public void reset(Reader reader, int position) { super.reset(reader, position); } public @Nullable IProject getProject() { return project; } public void setProject(@Nullable IProject project) { // Reset tokenizer if project changed (as tokenizer properties&settings // can depend on a specific project; see PHPTokenizer and // PhpScriptRegion). This way is more easy and safe than to propagate // the new project value on already-used tokenizers... // This method should only be used by class DocumentModelUtils. if (this.project != project) { fTokenizer = null; } this.project = project; } }