/******************************************************************************* * Copyright (c) 2011 Sierra Wireless 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: * Sierra Wireless - initial API and implementation *******************************************************************************/ package org.eclipse.koneki.ldt.core.internal.ast.parser; import java.util.ArrayList; import java.util.Hashtable; import java.util.Map; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.parser.AbstractSourceParser; import org.eclipse.dltk.ast.parser.IModuleDeclaration; import org.eclipse.dltk.compiler.env.IModuleSource; import org.eclipse.dltk.compiler.problem.DefaultProblem; import org.eclipse.dltk.compiler.problem.IProblem; import org.eclipse.dltk.compiler.problem.IProblemReporter; import org.eclipse.dltk.compiler.problem.ProblemCollector; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.ElementChangedEvent; import org.eclipse.dltk.core.IElementChangedListener; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IModelElementDelta; import org.eclipse.koneki.ldt.core.internal.Activator; import org.eclipse.koneki.ldt.core.internal.ast.models.LuaDLTKModelUtils; import org.eclipse.koneki.ldt.core.internal.ast.models.common.LuaSourceRoot; import org.eclipse.osgi.util.NLS; /** * Generates AST from Metalua analysis, {@link ASTNode}s are created straight from Lua * * @author Kevin KIN-FOO <kkinfoo@sierrawireless.com> */ public class LuaSourceParser extends AbstractSourceParser { private static ModelsBuilderLuaModule astBuilder = new ModelsBuilderLuaModule(); // BEGIN CACHE MANAGEMENT // TODO DLTK has already a cache system but it can be used to keep the last valid AST. // so we have to cache system. // Ideally, the parser should manage file with syntax errors.. private static Map<IModelElement, IModuleDeclaration> cache = new Hashtable<IModelElement, IModuleDeclaration>(); private static IElementChangedListener changedListener = new IElementChangedListener() { public void elementChanged(ElementChangedEvent event) { synchronized (LuaSourceParser.class) { IModelElementDelta delta = event.getDelta(); processDelta(delta); } } private void processDelta(IModelElementDelta delta) { IModelElement element = delta.getElement(); if (element.getElementType() == IModelElement.SOURCE_MODULE) { if (delta.getKind() == IModelElementDelta.REMOVED) { cache.remove(element); } else if (delta.getKind() == IModelElementDelta.CHANGED && delta.getFlags() == IModelElementDelta.F_PRIMARY_WORKING_COPY) { cache.remove(element); } } if (delta.getFlags() == IModelElementDelta.F_REMOVED_FROM_BUILDPATH) { if (delta.getAffectedChildren().length == 0) { for (IModelElement sourcemodule : new ArrayList<IModelElement>(cache.keySet())) { if (LuaDLTKModelUtils.isAncestor(sourcemodule, element)) { cache.remove(sourcemodule); } } } } if ((delta.getFlags() & IModelElementDelta.F_CHILDREN) != 0) { IModelElementDelta[] affectedChildren = delta.getAffectedChildren(); for (int i = 0; i < affectedChildren.length; i++) { IModelElementDelta child = affectedChildren[i]; processDelta(child); } } } }; static { DLTKCore.addElementChangedListener(changedListener); } // END CACHE MANAGEMENT public LuaSourceParser() { } /** * Generate DLTK AST straight from Lua * * @param input * Source to parse * @param reporter * Enable to report errors in parsed source code */ @Override public IModuleDeclaration parse(IModuleSource input, IProblemReporter reporter) { LuaSourceRoot module = new LuaSourceRoot(input.getSourceContents().length()); synchronized (LuaSourceParser.class) { try { // Build AST final String source = input.getSourceContents(); module = astBuilder.buildAST(source); /* * Handle encoding shifts */ // Compute encoding shifts final OffsetFixer fixer = new OffsetFixer(source); // Fix AST if (module != null) module.traverse(new EncodingVisitor(fixer)); // Fix problems if (reporter instanceof ProblemCollector) { for (final IProblem problem : ((ProblemCollector) reporter).getProblems()) { problem.setSourceStart(fixer.getCharacterPosition(problem.getSourceStart())); problem.setSourceEnd(fixer.getCharacterPosition(problem.getSourceEnd())); } } } // CHECKSTYLE:OFF catch (final Exception e) { // CHECKSTYLE:ON Activator.logWarning(NLS.bind("Unable to parse file {0}.", input.getFileName()), e); //$NON-NLS-1$ // the module is probably on error. if (module == null) module = new LuaSourceRoot(input.getSourceContents().length()); module.setProblem(1, 1, 0, "This file probably contains a syntax error."); //$NON-NLS-1$ } // Deal with errors on Lua side if (module != null) { // if module contains a syntax error if (module.hasError()) { // add error to repoter final DefaultProblem problem = module.getProblem(); problem.setOriginatingFileName(input.getFileName()); reporter.reportProblem(problem); // use AST in cache if (input.getModelElement() != null) { final LuaSourceRoot cached = (LuaSourceRoot) cache.get(input.getModelElement()); if (cached != null) { cached.setError(true); return cached; } } } else if (input.getModelElement() != null) { // if there are no error, put the new AST in cache cache.put(input.getModelElement(), module); } } } return module; } }