package com.aptana.editor.php.internal.parser; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org2.eclipse.php.internal.core.PHPVersion; import org2.eclipse.php.internal.core.ast.nodes.AST; import org2.eclipse.php.internal.core.ast.nodes.ASTParser; import org2.eclipse.php.internal.core.ast.nodes.Comment; import org2.eclipse.php.internal.core.ast.nodes.Program; import com.aptana.core.build.IBuildParticipant.BuildType; import com.aptana.core.logging.IdeLog; import com.aptana.core.util.IOUtil; import com.aptana.editor.php.PHPEditorPlugin; import com.aptana.editor.php.core.PHPVersionProvider; import com.aptana.editor.php.core.model.ISourceModule; import com.aptana.editor.php.epl.PHPEplPlugin; import com.aptana.editor.php.indexer.PHPGlobalIndexer; import com.aptana.editor.php.internal.core.builder.IModule; import com.aptana.editor.php.internal.model.utils.ModelUtils; import com.aptana.editor.php.internal.parser.nodes.NodeBuilder; import com.aptana.editor.php.internal.parser.nodes.NodeBuildingVisitor; import com.aptana.editor.php.internal.parser.nodes.PHPBlockNode; import com.aptana.editor.php.internal.parser.nodes.PHPCommentNode; import com.aptana.editor.php.internal.typebinding.TypeBindingBuilder; import com.aptana.parsing.AbstractParser; import com.aptana.parsing.IParseState; import com.aptana.parsing.WorkingParseResult; import com.aptana.parsing.ast.IParseNode; import com.aptana.parsing.ast.IParseRootNode; import com.aptana.parsing.ast.ParseNode; import com.aptana.parsing.ast.ParseRootNode; /** * PHP parser.<br> * This parser will only deal with PHP elements. Any other HTML elements are ignored (for now). * * @author Shalom Gibly <sgibly@aptana.com> * @since Aptana PHP 3.0 */ public class PHPParser extends AbstractParser { protected static final ParseNode[] NO_CHILDREN = new ParseNode[0]; private PHPVersion phpVersion; private IModule module; private ISourceModule sourceModule; private boolean parseHTML; private PHPParseRootNode latestValidNode; /** * Constructs a new PHPParser.<br> * By default, the node building that is done by the parser will involve HTML parsing as well. * * @see #PHPParser(PHPVersion, boolean) */ public PHPParser() { this(null); } /** * Constructs a new PHPParser with a preset PHPVersion.<br> * By default, the node building that is done by the parser will involve HTML parsing as well. * * @param phpVersion * @see #PHPParser(PHPVersion, boolean) */ public PHPParser(PHPVersion phpVersion) { this(phpVersion, true); } /** * Constructs a new PHPParser with a preset PHPVersion * * @param phpVersion * @param parseHTML * - indicate whether the node building that is done by the parser should involve HTML parsing as well. */ public PHPParser(PHPVersion phpVersion, boolean parseHTML) { this.phpVersion = phpVersion; this.parseHTML = parseHTML; } /** * Override the default implementation to provide support for PHP nodes inside JavaScript. */ protected void parse(IParseState parseState, WorkingParseResult working) // $codepro.audit.disable // declaredExceptions { String source = parseState.getSource(); int startingOffset = parseState.getStartingOffset(); PHPParseRootNode root = new PHPParseRootNode(NO_CHILDREN, startingOffset, startingOffset + source.length() - 1); Program program = null; if (parseState instanceof IPHPParseState) { IPHPParseState phpParseState = (IPHPParseState) parseState; phpVersion = phpParseState.getPHPVersion(); IModule newModule = phpParseState.getModule(); if (module != newModule) { module = newModule; sourceModule = phpParseState.getSourceModule(); latestValidNode = null; } aboutToBeReconciled(); } ASTParser parser = null; try { PHPVersion version = (phpVersion == null) ? PHPVersionProvider.getDefaultPHPVersion() : phpVersion; String buildType = null; try{ buildType = parseState.getBuildType(); }catch (Throwable e){ //防止com.aptana.parsing.IParseState没有更新, 没有getBuildType方法 } if(buildType!=null) { parser = ASTParser.newParser(new StringReader(source), version, true, sourceModule, BuildType.valueOf(buildType)); }else { parser = ASTParser.newParser(new StringReader(source), version, true, sourceModule); // $codepro.audit.disable } // closeWhereCreated program = parser.createAST(null); } catch (Exception e) { // TODO: handle exception IdeLog.logError(PHPEditorPlugin.getDefault(), "PHP parser error", e); //$NON-NLS-1$ } if (program != null) { processChildren(program, root, source); } // we maintain this value since it's being reset when the errors are flushed. boolean astHasErrors = false; if (program != null) { working.setParseResult(root); try { program.setSourceModule(ModelUtils.convertModule(module)); AST ast = program.getAST(); astHasErrors = ast.hasErrors(); if (astHasErrors) { working.setParseResult(null); } if (module != null) { // Pass in the latest source along with the module PHPGlobalIndexer.getInstance().processUnsavedModuleUpdate(program, module, source); } // Recalculate the type bindings TypeBindingBuilder.buildBindings(program, source); ast.flushErrors(); } catch (Throwable t) { IdeLog.logError(PHPEditorPlugin.getDefault(), "PHP parser error", t); //$NON-NLS-1$ } reconciled(program, false, new NullProgressMonitor()); } else { if (parser != null) { AST ast = parser.getAST(); if (ast != null) { astHasErrors = ast.hasErrors(); ast.flushErrors(); } } reconciled(null, false, new NullProgressMonitor()); } if (astHasErrors) { if (latestValidNode != null) { latestValidNode.setIsCached(true); } working.setParseResult(latestValidNode); return; } latestValidNode = root; working.setParseResult(root); } /** * Parse the PHP content, given as an input stream, and return a parse node that contains the children nodes that * were parsed. Note that this method does not use the parse state and does not update anything. * * @param source * @return * @throws java.lang.Exception * @see {@link #parse(IParseState)} */ public IParseRootNode parse(InputStream source) { String input = IOUtil.read(source); Program ast = parseAST(new StringReader(input)); // $codepro.audit.disable closeWhereCreated if (ast != null) { ParseRootNode root = new PHPParseRootNode(NO_CHILDREN, ast.getStart(), ast.getEnd()); // We have to pass in the source itself to support accurate PHPDoc display. processChildren(ast, root, input); return root; } return new PHPParseRootNode(NO_CHILDREN, 0, 0); } /** * Parse an return a {@link Program} (AST) for the given PHP source.<br> * This method is only parsing and does not update any state. * * @param reader * @return A {@link Program} AST; Null, in case an error occurred. */ public Program parseAST(Reader reader) { Program ast = null; try { PHPVersion version = (phpVersion == null) ? PHPVersionProvider.getDefaultPHPVersion() : phpVersion; // TODO - Perhaps we'll need to pass a preference value for the 'short-tags' instead of passing 'true' by // default. ASTParser parser = ASTParser.newParser(reader, version, true); ast = parser.createAST(null); } catch (Exception e) { IdeLog.logError(PHPEditorPlugin.getDefault(), "PHP parser error", e); //$NON-NLS-1$ } return ast; } /** * Notify the shared AST provider that the module is about to be reconciled. */ private void aboutToBeReconciled() { // Notify the shared AST provider PHPEplPlugin.getDefault().getASTProvider().aboutToBeReconciled(sourceModule); } /** * Notify the shared AST provider that the module was reconciled. */ private void reconciled(Program program, boolean forced, IProgressMonitor progressMonitor) { PHPEplPlugin.getDefault().getASTProvider().reconciled(program, sourceModule, progressMonitor); } /* * Process the AST and update the given IParseNode */ private void processChildren(Program ast, ParseRootNode root, String source) { /* * kept here for Debug purposes ApplyAll astPrinter = new ApplyAll() { * @Override public boolean apply(ASTNode node) { System.out.println(node.toString()); return true; } }; * ast.accept(astPrinter); */ List<IParseNode> commentNodes = new ArrayList<IParseNode>(); for (Comment c : ast.comments()) { commentNodes.add(new PHPCommentNode(c)); } root.setCommentNodes(commentNodes.toArray(new IParseNode[commentNodes.size()])); NodeBuilder builderClient = new NodeBuilder(source, false, parseHTML); ast.accept(new NodeBuildingVisitor(builderClient, source)); PHPBlockNode nodes = builderClient.populateNodes(); for (IParseNode child : nodes.getChildren()) { root.addChild(child); } } }