blob: 764ce982ff30225bbfd29fe2965b6be248e36eb6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 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.mylyn.docs.intent.parser.internal;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.mylyn.docs.intent.core.compiler.CompilationMessageType;
import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatus;
import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatusSeverity;
import org.eclipse.mylyn.docs.intent.core.compiler.CompilerFactory;
import org.eclipse.mylyn.docs.intent.core.document.IntentChapter;
import org.eclipse.mylyn.docs.intent.core.document.IntentDocument;
import org.eclipse.mylyn.docs.intent.core.document.IntentDocumentFactory;
import org.eclipse.mylyn.docs.intent.core.document.IntentGenericElement;
import org.eclipse.mylyn.docs.intent.core.document.IntentSection;
import org.eclipse.mylyn.docs.intent.parser.internal.state.IntentGenericState;
import org.eclipse.mylyn.docs.intent.parser.internal.state.SChapter;
import org.eclipse.mylyn.docs.intent.parser.internal.state.SDocument;
import org.eclipse.mylyn.docs.intent.parser.internal.state.SSection;
import org.eclipse.mylyn.docs.intent.parser.modelingunit.ParseException;
import org.eclipse.mylyn.docs.intent.serializer.IntentPositionManager;
/**
* Builder for an IntentStructuredElement : use a state machine to build Intent elements according to the
* signal sent by a IntentParser.
*
* @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
*/
public class IntentBuilder {
private IntentPositionManager positionManager;
/**
* CurrentRoot of the document : can be a IntentStructuredElement (Chapter, Section), a Modeling Unit or a
* Description Unit.
*/
private EObject currentRoot;
/**
* List of the created roots of the document.
*/
private List<EObject> roots;
/**
* Represents the current State of the parser.
*/
private IntentGenericState currentState;
/**
* Represents the currentImbricationLevel.
*/
private int currentImbricationLevel;
/**
* IntentBuilder constructor.
*
* @param positionManager
* the positionManager where to register positions
*/
public IntentBuilder(IntentPositionManager positionManager) {
roots = new ArrayList<EObject>();
this.positionManager = positionManager;
}
/**
* Returns the generated roots of the parsed content.
*
* @return the generated roots of the parsed content
*/
public List<EObject> getRoots() {
return roots;
}
/**
* Returns the generated root of the parsed content.
*
* @return the generated root of the parsed content
* @throws ParseException
* if there isn't exactly one generated root
*/
public EObject getRoot() throws ParseException {
if (roots.size() != 1) {
throw new ParseException("The elements described should be enclosed in a super element. ");
}
if (currentImbricationLevel > 0) {
addStatusOnElement(roots.get(0), "Syntax error : insert \"}\" to close this element.");
}
return roots.get(0);
}
/**
* Increases the current imbrication level.
*/
public void increaseImbricationLevel() {
this.currentImbricationLevel++;
}
/**
* Decreases the current imbrication level.
*/
public void decreaseImbricationLevel() {
this.currentImbricationLevel--;
}
/**
* Indicates the beginning of a Document.
*
* @param offset
* the begin offset of the document
* @param declarationLength
* the declaration length of the document
* @throws ParseException
* if the document can't be opened in the current state of the parser
*/
public void beginDocument(int offset, int declarationLength) throws ParseException {
if (currentState == null) {
currentRoot = IntentDocumentFactory.eINSTANCE.createIntentDocument();
roots.add(currentRoot);
currentState = new SDocument(offset, declarationLength, null, (IntentDocument)currentRoot,
positionManager);
increaseImbricationLevel();
} else {
throw new ParseException("Can't open any document here.");
}
}
/**
* Indicates the beginning of a Chapter.
*
* @param offset
* the begin offset of the Chapter
* @param declarationLength
* the declaration length of the Chapter
*/
public void beginChapter(int offset, int declarationLength) {
increaseImbricationLevel();
if (currentState == null) {
currentRoot = IntentDocumentFactory.eINSTANCE.createIntentChapter();
roots.add(currentRoot);
currentState = new SChapter(offset, declarationLength, null, (IntentChapter)currentRoot,
positionManager);
} else {
currentState = currentState.beginChapter(offset, declarationLength);
}
}
/**
* Indicates the beginning of a IntentSection.
*
* @param offset
* the begin offset of the Section
* @param declarationLength
* the declaration length of the Section
*/
public void beginSection(int offset, int declarationLength) {
increaseImbricationLevel();
if (currentState == null) {
currentRoot = IntentDocumentFactory.eINSTANCE.createIntentSection();
roots.add(currentRoot);
currentState = new SSection(offset, declarationLength, null, (IntentSection)currentRoot,
positionManager);
} else {
currentState = currentState.beginSection(offset, declarationLength);
}
}
/**
* Indicates the end of a Structured Element (Document, Chapter or Section).
*
* @param offset
* the ending offset of the structured element
* @throws ParseException
* if there is no structured element to end
*/
public void endStructuredElement(int offset) throws ParseException {
if (currentImbricationLevel == 0) {
throw new ParseException("There is no element to close.");
}
if (currentState != null) {
currentState = currentState.endStructuredElement(offset);
decreaseImbricationLevel();
} else {
addStatusOnElement(getRoot(), "Syntax Error on token \"{\" : no element to close.");
}
}
/**
* Adds a status with the given message on the given element.
*
* @param element
* the incorrect element
* @param statusMessage
* the message of the status to add
*/
public void addStatusOnElement(EObject element, String statusMessage) {
if (element instanceof IntentGenericElement) {
CompilationStatus compilationStatus = CompilerFactory.eINSTANCE.createCompilationStatus();
compilationStatus.setMessage(statusMessage);
compilationStatus.setSeverity(CompilationStatusSeverity.ERROR);
compilationStatus.setTarget((IntentGenericElement)element);
compilationStatus.setType(CompilationMessageType.INVALID_REFERENCE_ERROR);
((IntentGenericElement)element).getCompilationStatus().add(compilationStatus);
}
}
/**
* Indicates the detection of section options (visibility and header References).
*
* @param visibility
* the visibility of the section ("hidden", "internal" or null)
* @param headerReferences
* list of textual references to header declarations
*/
public void sectionOptions(String visibility, List<String> headerReferences) {
// System.out.println(getTab() + "IntentBuilder.sectionOptions() / " + currentState);
currentState = currentState.sectionOptions(visibility, headerReferences);
}
/**
* Indicates a Modeling Unit with the given content.
*
* @param offset
* the begin offset of the modeling unit
* @param modelingUnitContent
* the content of this modeling Unit
* @throws ParseException
* if the modeling unit parser detect any parse error
*/
public void modelingUnitContent(int offset, String modelingUnitContent) throws ParseException {
currentState = currentState.modelingUnitContent(offset, modelingUnitContent.length(),
formatUsingImbricationLevel(modelingUnitContent, true));
}
/**
* Indicates a Description Unit with the given Content.
*
* @param offset
* the begin offset of the description unit
* @param descriptionUnitContent
* the content of the description Unit
* @throws ParseException
* if the description unit parser detect any parse error.
*/
public void descriptionUnitContent(int offset, String descriptionUnitContent) throws ParseException {
// TODO FIXME sometimes currentState can be null
currentState = currentState.descriptionUnitContent(offset, descriptionUnitContent.length(),
formatUsingImbricationLevel(descriptionUnitContent, false));
}
/**
* Uses the current imbrication level to remove non-relevant tabulations.
*
* @param content
* the content to format
* @param isModelingUnit
* indicates if the content describes a modelingUnit
* @return the given content from which we removed any non-relevant tabulations
*/
private String formatUsingImbricationLevel(String content, boolean isModelingUnit) {
String intentContent = "";
String[] lines = content.split("\n");
// If the content is a Modeling Unit, we must temporary increase the imbrication level
int temporaryIncrease = 0;
if (isModelingUnit) {
temporaryIncrease++;
}
// For each line
for (int i = 0; i < lines.length; i++) {
String currentLine = lines[i];
// We remove the N first tabulations, with N equals to the currentImbricationLevel
for (int j = 0; j < this.currentImbricationLevel + temporaryIncrease; j++) {
if (currentLine.indexOf("\t") == 0) {
currentLine = currentLine.replaceFirst("\t", "");
}
}
intentContent += currentLine + "\n";
}
return intentContent;
}
}