blob: 49c8e8e2d8789b00f65bf5e770fdb221ef85e7af [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2012 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.parser;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.acceleo.internal.parser.ast.CST2ASTConverterWithDocumentationResolver;
import org.eclipse.acceleo.internal.parser.ast.IASTProvider;
import org.eclipse.acceleo.internal.parser.ast.ocl.OCLParser;
import org.eclipse.acceleo.internal.parser.cst.CSTParser;
import org.eclipse.acceleo.internal.parser.cst.utils.FileContent;
import org.eclipse.acceleo.internal.parser.documentation.IDocumentationProvider;
import org.eclipse.acceleo.parser.cst.CstFactory;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.ocl.Environment;
/**
* The source to parse. It can be created from a file or a buffer. The file property is null if this object
* has been created from a buffer. You can create an AST model with the method 'createAST'. An AST is created
* in a single resource. All the dependencies of this resource will be searched in the other resources of the
* same 'ResourceSet'. You can resolve the links between the different AST models of the current 'ResourceSet'
* with the method 'resolveAST'. The parsing issues are accessible by using the method 'getProblems'.
* <p>
* The resolution step must always be done at the end of the parsing of each file of the 'ResourceSet', like
* in the following example.
* <p>
* <br>
* Resource resource1 = resourceSet.createResource(uriTarget1);
* <p>
* Resource resource2 = resourceSet.createResource(uriTarget2);
* <p>
* AcceleoSourceBuffer source1 = new AcceleoSourceBuffer(file1);
* <p>
* AcceleoSourceBuffer source2 = new AcceleoSourceBuffer(file1);
* <p>
* source1.createAST(resource1);
* <p>
* source2.createAST(resource2);
* <p>
* <b> source1.resolveAST();
* <p>
* source2.resolveAST();
* <p>
* </b> </br>
*
* @author <a href="mailto:jonathan.musset@obeo.fr">Jonathan Musset</a>
*/
public class AcceleoSourceBuffer implements IASTProvider, IDocumentationProvider {
/**
* The AST model. It is created when you call the 'createAST' function.
*/
protected org.eclipse.acceleo.model.mtl.Module ast;
/**
* The AST creator. It is created when you call the 'createAST' function.
*
* @noreference This field is not intended to be referenced by clients.
*/
protected CST2ASTConverterWithDocumentationResolver astCreator;
/**
* The file to parse, can be null if the object is created with a buffer.
*/
private final AcceleoFile acceleoFile;
/**
* The buffer to parse.
*/
private final StringBuffer buffer;
/**
* This list will be initialized with the beginning offset of all lines in the given source buffer.
*
* @since 0.8
*/
private final List<Integer> lines = new ArrayList<Integer>();
/**
* Parsing problems.
*/
private final AcceleoParserProblems problems;
/**
* Parsing warnings.
*/
private final AcceleoParserWarnings warnings;
/**
* Parsing informations.
*/
private final AcceleoParserInfos infos;
/**
* The encoding of the buffer to parse.
*/
private final String encoding;
/**
* The CST model. It is created when you call the 'createAST' function.
*/
private org.eclipse.acceleo.parser.cst.Module cst;
/**
* Constructor.
*
* @param acceleoFile
* is the Acceleo file to parse
* @since 3.0
*/
public AcceleoSourceBuffer(AcceleoFile acceleoFile) {
this.acceleoFile = acceleoFile;
this.buffer = FileContent.getFileContent(getFile());
this.encoding = FileContent.getEncoding(buffer);
this.problems = new AcceleoParserProblems();
this.warnings = new AcceleoParserWarnings();
this.infos = new AcceleoParserInfos();
init();
}
/**
* Constructor.
*
* @param file
* is the file to parse
*/
public AcceleoSourceBuffer(File file) {
this(new AcceleoFile(file, AcceleoFile.simpleModuleName(file)));
}
/**
* Constructor.
*
* @param buffer
* is the buffer to parse, the file property will be null
*/
public AcceleoSourceBuffer(StringBuffer buffer) {
this.acceleoFile = null;
this.buffer = buffer;
this.encoding = FileContent.getEncoding(buffer);
this.problems = new AcceleoParserProblems();
this.warnings = new AcceleoParserWarnings();
this.infos = new AcceleoParserInfos();
init();
}
/**
* Initializes the line information for the underlying buffer.
*/
private void init() {
lines.add(Integer.valueOf(0));
int offset = 0;
while (offset < buffer.length()) {
if (buffer.charAt(offset) == '\n') {
lines.add(Integer.valueOf(offset));
} else if (buffer.charAt(offset) == '\r') {
lines.add(Integer.valueOf(offset));
if (buffer.length() > offset + 1 && buffer.charAt(offset + 1) == '\n') {
offset++;
}
}
offset++;
}
}
/***
* Returns the line number of the given offset.
*
* @param offset
* Offset we need to know the line of.
* @return The line number of the given offset.
* @since 0.8
*/
public int getLineOfOffset(int offset) {
int soughtLine = -1;
// shortcut
if (offset == 0) {
soughtLine = 0;
}
if (offset == buffer.length()) {
soughtLine = lines.size();
}
if (soughtLine == -1) {
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).intValue() > offset) {
soughtLine = i;
break;
}
}
}
if (soughtLine == -1) {
soughtLine = lines.size();
}
return soughtLine;
}
/**
* Returns the parsed file, can be null if the object is created with a buffer.
*
* @return the parsed file
*/
public File getFile() {
if (acceleoFile != null) {
return acceleoFile.getMtlFile();
}
return null;
}
/**
* Gets parsing issues.
*
* @return the parsing issues
*/
public AcceleoParserProblems getProblems() {
return problems;
}
/**
* Returns the string buffer to parse.
*
* @return the buffer
*/
public StringBuffer getBuffer() {
return buffer;
}
/**
* Returns the encoding of the buffer to parse.
*
* @return the encoding, or null if it isn't specified in the buffer
* @since 3.0
*/
public String getEncoding() {
return encoding;
}
/**
* Gets the AST model. It is created when you call the 'createAST' function.
*
* @return the AST model
*/
public org.eclipse.acceleo.model.mtl.Module getAST() {
return ast;
}
/**
* Gets the CST model. It is created when you call the 'createCST' function.
*
* @return the CST model
*/
public org.eclipse.acceleo.parser.cst.Module getCST() {
return cst;
}
/**
* Gets the OCL parser (from the OCL plug-in).
*
* @return the OCL parser, can be null
* @deprecated Unused, this will be removed beefore 3.0 is released.
* @noreference This method is not intended to be referenced by clients.
* @nooverride This method is not intended to be re-implemented or extended by clients.
*/
@Deprecated
public OCLParser getOCL() {
if (astCreator != null) {
return astCreator.getOCL();
}
return null;
}
/**
* Returns the environment instance that was used under the covers by the ocl parser. It returns null when
* we don't know the OCL environment.
*
* @return The environment instance that was used under the covers by the ocl parser, can be null
* @since 3.0
*/
public Environment<?, EClassifier, EOperation, EStructuralFeature, ?, ?, ?, ?, ?, ?, ?, ?> getOCLEnvironment() {
if (astCreator != null && astCreator.getOCL() != null) {
return astCreator.getOCL().getOCLEnvironment();
}
return null;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.parser.ast.IASTProvider#createAST(org.eclipse.emf.ecore.resource.Resource)
*/
public void createAST(Resource resource) {
if (cst == null) {
createCST();
}
astCreator = new CST2ASTConverterWithDocumentationResolver();
astCreator.setASTProvider(this);
astCreator.createAST(cst, resource);
if (resource.getContents().size() > 0
&& resource.getContents().get(0) instanceof org.eclipse.acceleo.model.mtl.Module) {
ast = (org.eclipse.acceleo.model.mtl.Module)resource.getContents().get(0);
if (ast != null && acceleoFile != null) {
ast.setNsURI(acceleoFile.getFullModuleName());
}
} else {
ast = null;
}
}
/**
* Ensure to compute the whole AST before to use it.
*/
public void refreshAST() {
ast = null;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.parser.ast.IASTProvider#resolveAST()
*/
public void resolveAST() {
if (astCreator != null) {
astCreator.resolveAST(cst);
}
}
/**
* Resolution step of the AST creation : OCL and invocations. This function must be called after
* 'createAST'. The resolution step can be limited to the specified region, for increasing performances.
* Remark : The -1 value means everywhere.
*
* @param posBegin
* is the lower bound
* @param posEnd
* is the upper bound
*/
public void resolveAST(int posBegin, int posEnd) {
if (astCreator != null) {
astCreator.resolveAST(cst, posBegin, posEnd);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.parser.ast.IASTProvider#createCST()
*/
public void createCST() {
ast = null;
CSTParser parser = new CSTParser(this);
cst = parser.parse();
if (cst == null) {
cst = CstFactory.eINSTANCE.createModule();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.internal.parser.ast.IASTProvider#log(java.lang.String, int, int)
* @deprecated Use {@link #logProblem(String, int, int)} instead.
*/
@Deprecated
public void log(String message, int posBegin, int posEnd) {
this.logProblem(message, posBegin, posEnd);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.internal.parser.ast.IASTProvider2#logProblem(java.lang.String, int, int)
* @since 3.1
*/
public void logProblem(final String message, final int posBegin, final int posEnd) {
int[] pos = trim(posBegin, posEnd);
int line = FileContent.lineNumber(buffer, pos[0]);
problems.addProblem(getFile(), message, line, pos[0], pos[1]);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.internal.parser.ast.IASTProvider2#logWarning(java.lang.String, int, int)
* @since 3.1
*/
public void logWarning(final String message, final int posBegin, final int posEnd) {
int[] pos = trim(posBegin, posEnd);
int line = FileContent.lineNumber(this.buffer, pos[0]);
this.warnings.addWarning(message, line, pos[0], pos[1]);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.internal.parser.ast.IASTProvider2#logInfo(java.lang.String, int, int)
* @since 3.1
*/
public void logInfo(final String message, final int posBegin, final int posEnd) {
int[] pos = trim(posBegin, posEnd);
int line = FileContent.lineNumber(this.buffer, pos[0]);
this.infos.addInfo(message, line, pos[0], pos[1]);
}
/**
* Returns the warnings.
*
* @return the warnings
* @since 3.1
*/
public AcceleoParserWarnings getWarnings() {
return this.warnings;
}
/**
* Returns the infos.
*
* @return the infos
* @since 3.1
*/
public AcceleoParserInfos getInfos() {
return this.infos;
}
/**
* Gets the smallest range that contains all the significant characters. Returns an array with 2 elements
* {trim.begin, trim.end}.
*
* @see java.lang.String#trim()
* @param posBegin
* is the beginning index of the range
* @param posEnd
* is the ending index of the range
* @return the smallest range that contains all the significant characters, the length of the array is
* always 2
*/
public int[] trim(int posBegin, int posEnd) {
int b;
if (posBegin < 0) {
b = 0;
} else {
b = posBegin;
}
int e;
if (posEnd < 0 || posEnd > buffer.length()) {
e = buffer.length();
} else {
e = posEnd;
}
while (b < e && Character.isWhitespace(buffer.charAt(b))) {
b++;
}
if (!((posEnd < 0 || b == e) && b + 1 < buffer.length())) {
while ((e > b + 1) && Character.isWhitespace(buffer.charAt(e - 1))) {
e--;
}
}
return new int[] {b, e };
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.internal.parser.documentation.IDocumentationProvider#resolveASTDocumentation()
* @since 3.1
*/
public void resolveASTDocumentation() {
if (this.astCreator != null && this.ast != null) {
this.astCreator.resolveASTDocumentation(this.ast, this.buffer);
}
}
}