Skip to content
Snippets Groups Projects
Verified Commit 80c5ccff authored by Rico Bergmann's avatar Rico Bergmann
Browse files

Implement the ModelJoin parser using ANTLR

parent c8e98ac6
No related branches found
No related tags found
1 merge request!4Include ModelJoin support in main development
Showing
with 899 additions and 177 deletions
...@@ -48,3 +48,5 @@ lazy val generator = (project in file(".")) ...@@ -48,3 +48,5 @@ lazy val generator = (project in file("."))
).enablePlugins(Antlr4Plugin) ).enablePlugins(Antlr4Plugin)
antlr4PackageName in Antlr4 := Some("org.rosi_project.model_sync.model_join.representation.parser.antlr.generated") antlr4PackageName in Antlr4 := Some("org.rosi_project.model_sync.model_join.representation.parser.antlr.generated")
antlr4GenVisitor in Antlr4 := true
antlr4GenListener in Antlr4 := false
sbt.version = 1.2.1 sbt.version = 1.2.7
\ No newline at end of file
...@@ -19,7 +19,7 @@ join : (naturaljoin | thetajoin | outerjoin) AS classres ...@@ -19,7 +19,7 @@ join : (naturaljoin | thetajoin | outerjoin) AS classres
CLOSEDCURLYBRAKET )? ; CLOSEDCURLYBRAKET )? ;
naturaljoin : NATURAL JOIN classres WITH classres ; naturaljoin : NATURAL JOIN classres WITH classres ;
thetajoin : THETA JOIN classres WITH classres WHERE oclcond; thetajoin : THETA JOIN classres WITH classres WHERE oclcond;
outerjoin : (leftouterjoin | rightouterjoin) OUTER JOIN WITH classres; outerjoin : (leftouterjoin | rightouterjoin) OUTER JOIN classres WITH classres;
leftouterjoin : LEFT ; leftouterjoin : LEFT ;
rightouterjoin : RIGHT ; rightouterjoin : RIGHT ;
...@@ -34,8 +34,8 @@ keepexpr : (keeptypeexpr | keepoutgoingexpr | keepincomingexpr) ...@@ -34,8 +34,8 @@ keepexpr : (keeptypeexpr | keepoutgoingexpr | keepincomingexpr)
keepexpr* keepexpr*
CLOSEDCURLYBRAKET)? ; CLOSEDCURLYBRAKET)? ;
keeptypeexpr : keepsupertypeexpr | keepsubtypeexpr ; keeptypeexpr : keepsupertypeexpr | keepsubtypeexpr ;
keepsupertypeexpr : KEEP SUPERTYPE ( AS TYPE classres )? ; keepsupertypeexpr : KEEP SUPERTYPE classres ( AS TYPE classres )? ;
keepsubtypeexpr : KEEP SUBTYPE ( AS TYPE classres )? ; keepsubtypeexpr : KEEP SUBTYPE classres ( AS TYPE classres )? ;
keepoutgoingexpr : KEEP OUTGOING attrres ( AS TYPE classres )? ; keepoutgoingexpr : KEEP OUTGOING attrres ( AS TYPE classres )? ;
keepincomingexpr : KEEP INCOMING attrres (AS TYPE classres )? ; keepincomingexpr : KEEP INCOMING attrres (AS TYPE classres )? ;
......
...@@ -225,6 +225,11 @@ public class KeepAggregateExpression extends KeepExpression { ...@@ -225,6 +225,11 @@ public class KeepAggregateExpression extends KeepExpression {
return target; return target;
} }
@Override
public void accept(@Nonnull KeepExpressionVisitor visitor) {
visitor.visit(this);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
...@@ -49,6 +49,11 @@ public class KeepAttributesExpression extends KeepExpression { ...@@ -49,6 +49,11 @@ public class KeepAttributesExpression extends KeepExpression {
return new ArrayList<>(attributes); return new ArrayList<>(attributes);
} }
@Override
public void accept(@Nonnull KeepExpressionVisitor visitor) {
visitor.visit(this);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
...@@ -94,6 +94,11 @@ public class KeepCalculatedExpression extends KeepExpression { ...@@ -94,6 +94,11 @@ public class KeepCalculatedExpression extends KeepExpression {
return target; return target;
} }
@Override
public void accept(@Nonnull KeepExpressionVisitor visitor) {
visitor.visit(this);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
package org.rosi_project.model_sync.model_join.representation.grammar; package org.rosi_project.model_sync.model_join.representation.grammar;
import javax.annotation.Nonnull;
/** /**
* A {@code KeepExpression} configures the resulting model of a join. * A {@code KeepExpression} configures the resulting model of a join.
* <p> * <p>
...@@ -10,6 +12,49 @@ package org.rosi_project.model_sync.model_join.representation.grammar; ...@@ -10,6 +12,49 @@ package org.rosi_project.model_sync.model_join.representation.grammar;
*/ */
public abstract class KeepExpression { public abstract class KeepExpression {
/**
* The {@code visitor} enables the execution of arbitrary algorithms on {@code KeepExpression}s
* without taking care of the actual traversal of the expression tree.
*/
public interface KeepExpressionVisitor {
/**
* Runs the algorithm as appropriate for {@link KeepAggregateExpression}s.
*/
void visit(@Nonnull KeepAggregateExpression expression);
/**
* Runs the algorithm as appropriate for {@link KeepAttributesExpression}s.
*/
void visit(@Nonnull KeepAttributesExpression expression);
/**
* Runs the algorithm as appropriate for {@link KeepCalculatedExpression}s.
*/
void visit(@Nonnull KeepCalculatedExpression expression);
/**
* Runs the algorithm as appropriate for {@link KeepReferenceExpression}s.
*/
void visit(@Nonnull KeepReferenceExpression expression);
/**
* Runs the algorithm as appropriate for {@link KeepSubTypeExpression}s.
*/
void visit(@Nonnull KeepSubTypeExpression expression);
/**
* Runs the algorithm as appropriate for {@link KeepSuperTypeExpression}s.
*/
void visit(@Nonnull KeepSuperTypeExpression expression);
}
// TODO `as` statements may be omitted !?!? ... (╯°□°)╯︵ ┻━┻ // TODO `as` statements may be omitted !?!? ... (╯°□°)╯︵ ┻━┻
/**
* Applies a {@code visitor} to {@code this} expression structure.
*/
public abstract void accept(@Nonnull KeepExpressionVisitor visitor);
} }
...@@ -5,6 +5,7 @@ import java.util.List; ...@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.rosi_project.model_sync.model_join.representation.core.AttributePath; import org.rosi_project.model_sync.model_join.representation.core.AttributePath;
import org.rosi_project.model_sync.model_join.representation.core.ClassResource;
import org.rosi_project.model_sync.model_join.representation.util.Assert; import org.rosi_project.model_sync.model_join.representation.util.Assert;
// TODO the Xtext grammar also specifies an optional `as reference` part - what does that part do? // TODO the Xtext grammar also specifies an optional `as reference` part - what does that part do?
...@@ -56,7 +57,7 @@ public class KeepReferenceExpression extends KeepExpression { ...@@ -56,7 +57,7 @@ public class KeepReferenceExpression extends KeepExpression {
private ReferenceDirection referenceDirection; private ReferenceDirection referenceDirection;
private AttributePath attribute; private AttributePath attribute;
private AttributePath target; private ClassResource target;
@Nonnull @Nonnull
private List<KeepExpression> keeps; private List<KeepExpression> keeps;
...@@ -92,7 +93,7 @@ public class KeepReferenceExpression extends KeepExpression { ...@@ -92,7 +93,7 @@ public class KeepReferenceExpression extends KeepExpression {
* Specifies the name of the attribute in the target join. * Specifies the name of the attribute in the target join.
*/ */
@Nonnull @Nonnull
public KeepReferenceBuilder as(@Nonnull AttributePath target) { public KeepReferenceBuilder as(@Nonnull ClassResource target) {
this.target = target; this.target = target;
return this; return this;
} }
...@@ -131,7 +132,7 @@ public class KeepReferenceExpression extends KeepExpression { ...@@ -131,7 +132,7 @@ public class KeepReferenceExpression extends KeepExpression {
private final ReferenceDirection referenceDirection; private final ReferenceDirection referenceDirection;
@Nonnull @Nonnull
private final AttributePath target; private final ClassResource target;
@Nonnull @Nonnull
private final List<KeepExpression> keeps; private final List<KeepExpression> keeps;
...@@ -142,14 +143,13 @@ public class KeepReferenceExpression extends KeepExpression { ...@@ -142,14 +143,13 @@ public class KeepReferenceExpression extends KeepExpression {
* @param attribute the attribute of the source model which contains the references * @param attribute the attribute of the source model which contains the references
* @param referenceDirection whether the source model owns the reference or is being * @param referenceDirection whether the source model owns the reference or is being
* referenced * referenced
* @param target the name of the attribute under which the resulting references should be made * @param target the name of the class which enca
* available
* @param keeps the statements which should be build the "nested" view for the references * @param keeps the statements which should be build the "nested" view for the references
*/ */
public KeepReferenceExpression( public KeepReferenceExpression(
@Nonnull AttributePath attribute, @Nonnull AttributePath attribute,
@Nonnull ReferenceDirection referenceDirection, @Nonnull ReferenceDirection referenceDirection,
@Nonnull AttributePath target, @Nonnull ClassResource target,
@Nonnull List<KeepExpression> keeps) { @Nonnull List<KeepExpression> keeps) {
this.attribute = attribute; this.attribute = attribute;
this.referenceDirection = referenceDirection; this.referenceDirection = referenceDirection;
...@@ -179,7 +179,7 @@ public class KeepReferenceExpression extends KeepExpression { ...@@ -179,7 +179,7 @@ public class KeepReferenceExpression extends KeepExpression {
* available. * available.
*/ */
@Nonnull @Nonnull
public AttributePath getTarget() { public ClassResource getTarget() {
return target; return target;
} }
...@@ -192,6 +192,11 @@ public class KeepReferenceExpression extends KeepExpression { ...@@ -192,6 +192,11 @@ public class KeepReferenceExpression extends KeepExpression {
return new ArrayList<>(keeps); return new ArrayList<>(keeps);
} }
@Override
public void accept(@Nonnull KeepExpressionVisitor visitor) {
visitor.visit(this);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
...@@ -136,6 +136,11 @@ public class KeepSubTypeExpression extends KeepExpression { ...@@ -136,6 +136,11 @@ public class KeepSubTypeExpression extends KeepExpression {
return new ArrayList<>(keeps); return new ArrayList<>(keeps);
} }
@Override
public void accept(@Nonnull KeepExpressionVisitor visitor) {
visitor.visit(this);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
...@@ -135,6 +135,11 @@ public class KeepSuperTypeExpression extends KeepExpression { ...@@ -135,6 +135,11 @@ public class KeepSuperTypeExpression extends KeepExpression {
return new ArrayList<>(keeps); return new ArrayList<>(keeps);
} }
@Override
public void accept(@Nonnull KeepExpressionVisitor visitor) {
visitor.visit(this);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
package org.rosi_project.model_sync.model_join.representation.parser; package org.rosi_project.model_sync.model_join.representation.parser;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Pattern;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression; import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
import org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder;
/** /**
* The {@code ModelJoinParser} reads a {@code ModelJoin} file from disk and creates matching * The {@code ModelJoinParser} reads a {@code ModelJoin} file from disk and creates a matching
* representation for it. * logical representation for it.
* *
* @author Rico Bergmann * @author Rico Bergmann
*/ */
public class ModelJoinParser { public interface ModelJoinParser {
/** /**
* Converts the {@code ModelJoin} file into a {@link ModelJoinExpression}, wrapped into an {@code * Converts the {@code ModelJoin} file into a {@link ModelJoinExpression}, wrapped into an {@code
* Optional}. If parsing fails, an empty {@code Optional} will be returned. * Optional}. If parsing fails, an empty {@code Optional} will be returned.
*/ */
@Nonnull @Nonnull
public static Optional<ModelJoinExpression> read(@Nonnull File modelFile) { Optional<ModelJoinExpression> read(@Nonnull File modelFile);
ModelJoinParser parser = new ModelJoinParser(modelFile, ErrorReportingStrategy.OPTIONAL);
return parser.run();
}
/** /**
* Converts the {@code ModelJoin} file into a {@link ModelJoinExpression}. If parsing fails, a * Converts the {@code ModelJoin} file into a {@link ModelJoinExpression}. If parsing fails, a
...@@ -36,149 +27,6 @@ public class ModelJoinParser { ...@@ -36,149 +27,6 @@ public class ModelJoinParser {
* @throws ModelJoinParsingException if the model may not be parsed for some reason. * @throws ModelJoinParsingException if the model may not be parsed for some reason.
*/ */
@Nonnull @Nonnull
public static ModelJoinExpression readOrThrow(@Nonnull File modelFile) { ModelJoinExpression readOrThrow(@Nonnull File modelFile);
ModelJoinParser parser = new ModelJoinParser(modelFile, ErrorReportingStrategy.EXCEPTION);
Optional<ModelJoinExpression> result = parser.run();
if (result.isPresent()) {
return result.get();
} else {
/*
* Theoretically, the parser should throw the exception by itself, so this code should never
* actually run. It is merely here for safety purposes (unchecked access of Optional.get())
*/
throw new ModelJoinParsingException("Result not present but no exception was thrown either");
}
}
/**
* Indicates what should happen if parsing fails.
*/
enum ErrorReportingStrategy {
/**
* On failure an empty {@code Optional} should be returned.
*/
OPTIONAL,
/**
* On failure an {@link ModelJoinParsingException} should be thrown.
*/
EXCEPTION
}
private static final Pattern EMPTY_LINE = Pattern.compile("^\\s*$");
private static final Pattern IMPORT_STATEMENT = Pattern.compile("^import .*");
private static final Pattern TARGET_STATEMENT = Pattern.compile("^target .*");
private static final Pattern JOIN_STATEMENT = Pattern
.compile("^(((left|right) outer)|theta|natural) join .*");
@Nonnull
private final File modelFile;
@Nonnull
private final ErrorReportingStrategy errorReportingStrategy;
/**
* Full constructor.
*
* @param modelFile the file to parse
* @param errorReportingStrategy what to do in case an error occurs.
*/
private ModelJoinParser(@Nonnull File modelFile,
@Nonnull ErrorReportingStrategy errorReportingStrategy) {
this.modelFile = modelFile;
this.errorReportingStrategy = errorReportingStrategy;
}
/**
* Creates the {@code ModelJoin} representation from the {@code modelFile}.
* <p>
* Depending on the selected {@code errorReportingStrategy} either an Exception will be thrown, or
* an empty {@code Optional} will be returned if something goes wrong.
*/
private Optional<ModelJoinExpression> run() {
ModelJoinExpression resultingModel;
try (FileInputStream modelInputStream = new FileInputStream(modelFile);
InputStreamReader modelInputReader = new InputStreamReader(modelInputStream);
BufferedReader bufferedModelReader = new BufferedReader(modelInputReader)) {
ModelJoinBuilder modelJoinBuilder = ModelJoinBuilder.createNewModelJoin();
readModelFileAndPopulateBuilder(bufferedModelReader, modelJoinBuilder);
resultingModel = modelJoinBuilder.build();
} catch (ModelJoinParsingException e) {
switch (errorReportingStrategy) {
case OPTIONAL:
return Optional.empty();
case EXCEPTION:
throw e;
default:
throw new AssertionError(errorReportingStrategy);
}
} catch (IOException e) {
switch (errorReportingStrategy) {
case OPTIONAL:
return Optional.empty();
case EXCEPTION:
throw new ModelJoinParsingException(e);
default:
throw new AssertionError(errorReportingStrategy);
}
}
return Optional.of(resultingModel);
}
/**
* Reads the {@code modelFile} and constructs the corresponding {@code ModelJoin} on the fly.
* <p>
* On error, an {@code IOException} or an {@link ModelJoinParsingException} will be thrown.
*
* @throws ModelJoinParsingException if the model file's content are malformed or another
* component failed
* @throws IOException if the model file might not be read any further
*/
private void readModelFileAndPopulateBuilder(BufferedReader modelReader,
ModelJoinBuilder modelBuilder) throws IOException {
String currentLine;
while ((currentLine = modelReader.readLine()) != null) {
if (lineShouldBeSkipped(currentLine)) {
// the line should be skipped. So we do nothing.
continue;
} else if (lineStartsJoinDeclaration(currentLine)) {
// a new join is being declared. Delegate to the join parser to handle the rest.
JoinParser joinParser = new JoinParser(currentLine, modelReader);
modelBuilder.add(joinParser.run());
} else {
// we do not know what to do with the current line. Should abort.
throw new ModelJoinParsingException("Unexpected line: '" + currentLine + "'");
}
}
}
/**
* Checks, whether the parser should consider the given {@code line} any further, or just ignore
* it.
* <p>
* A line should be skipped, iff.
* <ul>
* <li>It only consists of whitespace characters</li>
* <li>It is an {@code import statement} (as those are not yet considered in the current {@code
* ModelJoin} abstraction)</li>
* <li>It is a {@code target statement} (as those are not yet considered in the current {@code
* ModelJoin} abstraction)</li>
* </ul>
*/
private boolean lineShouldBeSkipped(String line) {
return EMPTY_LINE.matcher(line).matches() //
|| IMPORT_STATEMENT.matcher(line).matches() //
|| TARGET_STATEMENT.matcher(line).matches();
}
/**
* Checks, whether the given {@code line} is the beginning of a {@code join statement}.
*/
private boolean lineStartsJoinDeclaration(String line) {
return JOIN_STATEMENT.matcher(line).matches();
}
} }
package org.rosi_project.model_sync.model_join.representation.parser; package org.rosi_project.model_sync.model_join.representation.parser;
/** /**
* The {@code ModelJoinParsingException} indicates that a file could not be read into a * The {@code ModelJoinParsingException} indicates that a {@code ModelJoin} file could not be read
* {@code ModelJoin} instance correctly. * correctly, e.g. because of missing values or a malformed structure.
* *
* @author Rico Bergmann * @author Rico Bergmann
*/ */
...@@ -11,21 +11,21 @@ public class ModelJoinParsingException extends RuntimeException { ...@@ -11,21 +11,21 @@ public class ModelJoinParsingException extends RuntimeException {
/** /**
* @see RuntimeException#RuntimeException(String) * @see RuntimeException#RuntimeException(String)
*/ */
ModelJoinParsingException(String message) { public ModelJoinParsingException(String message) {
super(message); super(message);
} }
/** /**
* @see RuntimeException#RuntimeException(String, Throwable) * @see RuntimeException#RuntimeException(String, Throwable)
*/ */
ModelJoinParsingException(String message, Throwable cause) { public ModelJoinParsingException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
/** /**
* @see RuntimeException#RuntimeException(Throwable) * @see RuntimeException#RuntimeException(Throwable)
*/ */
ModelJoinParsingException(Throwable cause) { public ModelJoinParsingException(Throwable cause) {
super(cause); super(cause);
} }
} }
package org.rosi_project.model_sync.model_join.representation.parser.antlr;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
import javax.annotation.Nonnull;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.TokenStream;
import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParser;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParsingException;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinLexer;
/**
* The {@code AntlrBackedModelJoinParser} reads a {@code ModelJoin} file according to a compiled
* {@code ANTLR} grammar and generates the matching {@link ModelJoinExpression} for the file.
*
* @author Rico Bergmann
* @see <a href="https://www.antlr.org/">ANTLR project</a>
*/
public class AntlrBackedModelJoinParser implements ModelJoinParser {
@Nonnull
@Override
public Optional<ModelJoinExpression> read(@Nonnull File modelFile) {
try {
return Optional.of(invokeAntlr(modelFile));
} catch (ModelJoinParsingException e) {
return Optional.empty();
}
}
@Nonnull
@Override
public ModelJoinExpression readOrThrow(@Nonnull File modelFile) {
return invokeAntlr(modelFile);
}
/**
* Executes the whole ANTLR workflow.
*
* @throws ModelJoinParsingException if the workflow fails. Details on the error will be added
* to the exception.
*/
protected ModelJoinExpression invokeAntlr(@Nonnull File modelFile) {
CharStream modelStream = consumeCompleteModelFileContent(modelFile);
ModelJoinLexer lexer = initializeLexerBasedOn(modelStream);
TokenStream tokenStream = tokenize(lexer);
org.rosi_project.model_sync.model_join.representation.parser.antlr.generated. //
ModelJoinParser modelJoinParser = initializeParserBasedOn(tokenStream);
org.rosi_project.model_sync.model_join.representation.parser.antlr.generated. //
ModelJoinParser.ModeljoinContext modelJoinContext = modelJoinParser.modeljoin();
ModelJoinVisitor modelJoinVisitor = new ModelJoinVisitor();
return modelJoinVisitor.visitModeljoin(modelJoinContext);
}
/**
* Converts the content of a {@code ModelJoin} file into an ANTLR input stream.
*
* @throws ModelJoinParsingException if the file may not be read
*/
@Nonnull
protected CharStream consumeCompleteModelFileContent(@Nonnull File modelFile) {
try {
return CharStreams.fromPath(modelFile.toPath());
} catch (IOException e) {
throw new ModelJoinParsingException(e);
}
}
/**
* Sets up a lexer for the given {@code ModelJoin} file.
*/
@Nonnull
protected ModelJoinLexer initializeLexerBasedOn(@Nonnull CharStream modelFile) {
return new ModelJoinLexer(modelFile);
}
/**
* Executes the lexing step.
*/
@Nonnull
protected TokenStream tokenize(@Nonnull ModelJoinLexer lexer) {
return new CommonTokenStream(lexer);
}
/**
* Sets up the parser.
*/
@Nonnull
protected org.rosi_project.model_sync.model_join.representation.parser.antlr.generated. //
ModelJoinParser initializeParserBasedOn(@Nonnull TokenStream tokens) {
return new org.rosi_project.model_sync.model_join.representation.parser.antlr.generated. //
ModelJoinParser(tokens);
}
}
package org.rosi_project.model_sync.model_join.representation.parser.antlr;
import javax.annotation.Nonnull;
import org.rosi_project.model_sync.model_join.representation.core.ClassResource;
import org.rosi_project.model_sync.model_join.representation.core.OCLConstraint;
import org.rosi_project.model_sync.model_join.representation.grammar.JoinExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.JoinExpression.JoinType;
import org.rosi_project.model_sync.model_join.representation.grammar.NaturalJoinExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.OuterJoinExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.ThetaJoinExpression;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParsingException;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinBaseVisitor;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.JoinContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.NaturaljoinContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.OuterjoinContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.ThetajoinContext;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.NaturalJoinBuilder;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.OuterJoinBuilder;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.ThetaJoinBuilder;
/**
* The {@code JoinStatementVisitor} creates {@link JoinExpression} instances for a parsed {@code
* ModelJoin} file.
*
* @author Rico Bergmann
*/
public class JoinStatementVisitor extends ModelJoinBaseVisitor<JoinExpression> {
private final KeepStatementVisitor keepStatementVisitor;
/**
* Default constructor. Nothing special about it.
*/
JoinStatementVisitor() {
this.keepStatementVisitor = new KeepStatementVisitor();
}
@Override
public JoinExpression visitJoin(JoinContext ctx) {
JoinType joinType = determineJoinType(ctx);
return invokeJoinBuilderAccordingTo(joinType, ctx);
}
/**
* Checks, which kind of join is specified in a specific {@code JoinContext}.
*/
private JoinType determineJoinType(@Nonnull JoinContext ctx) {
if (ctx.thetajoin() != null) {
return JoinType.THETA;
} else if (ctx.naturaljoin() != null) {
return JoinType.NATURAL;
} else if (ctx.outerjoin() != null) {
return JoinType.OUTER;
} else {
throw new ModelJoinParsingException("Unkown join type in join " + ctx);
}
}
/**
* Generates a {@link JoinExpression} according to the {@link JoinType} of the given {@code
* JoinContext}.
*/
private JoinExpression invokeJoinBuilderAccordingTo(@Nonnull JoinType type,
@Nonnull JoinContext ctx) {
switch (type) {
case THETA:
return generateThetaJoin(ctx);
case NATURAL:
return generateNaturalJoin(ctx);
case OUTER:
return generateOuterJoin(ctx);
default:
throw new AssertionError(type);
}
}
/**
* Creates the {@link ThetaJoinExpression} instance contained in a {@code JoinContext}. This
* method assumes that the provided {@code ctx} indeed contains a theta join.
*/
private ThetaJoinExpression generateThetaJoin(@Nonnull JoinContext ctx) {
ThetajoinContext thetajoinContext = ctx.thetajoin();
ClassResource left = ClassResource.fromQualifiedName(thetajoinContext.classres(0).getText());
ClassResource right = ClassResource.fromQualifiedName(thetajoinContext.classres(1).getText());
ThetaJoinBuilder joinBuilder = JoinFactory.createNew()
.theta()
.join(left)
.with(right)
.as(ClassResource.fromQualifiedName(ctx.classres().getText()))
.where(OCLConstraint.of(thetajoinContext.oclcond().getText()));
ctx.keepaggregatesexpr().stream()
.map(keepStatementVisitor::visitKeepaggregatesexpr)
.forEach(joinBuilder::keep);
ctx.keepattributesexpr().stream()
.map(keepStatementVisitor::visitKeepattributesexpr)
.forEach(joinBuilder::keep);
ctx.keepexpr().stream()
.map(keepStatementVisitor::visitKeepexpr)
.forEach(joinBuilder::keep);
return joinBuilder.done();
}
/**
* Creates the {@link NaturalJoinExpression} instance contained in a {@code JoinContext}. This
* method assumes that the provided {@code ctx} indeed contains a natural join.
*/
private NaturalJoinExpression generateNaturalJoin(@Nonnull JoinContext ctx) {
NaturaljoinContext naturalJoinContext = ctx.naturaljoin();
ClassResource left = ClassResource.fromQualifiedName(naturalJoinContext.classres(0).getText());
ClassResource right = ClassResource.fromQualifiedName(naturalJoinContext.classres(1).getText());
NaturalJoinBuilder joinBuilder = JoinFactory.createNew()
.natural()
.join(left)
.with(right)
.as(ClassResource.fromQualifiedName(ctx.classres().getText()));
ctx.keepaggregatesexpr().stream()
.map(keepStatementVisitor::visitKeepaggregatesexpr)
.forEach(joinBuilder::keep);
ctx.keepattributesexpr().stream()
.map(keepStatementVisitor::visitKeepattributesexpr)
.forEach(joinBuilder::keep);
ctx.keepexpr().stream()
.map(keepStatementVisitor::visitKeepexpr)
.forEach(joinBuilder::keep);
return joinBuilder.done();
}
/**
* Creates the {@link OuterJoinExpression} instance contained in a {@code JoinContext}. This
* method assumes that the provided {@code ctx} indeed contains an outer join.
*/
private OuterJoinExpression generateOuterJoin(@Nonnull JoinContext ctx) {
OuterjoinContext outerJoinContext = ctx.outerjoin();
ClassResource left = ClassResource.fromQualifiedName(outerJoinContext.classres(0).getText());
ClassResource right = ClassResource.fromQualifiedName(outerJoinContext.classres(1).getText());
OuterJoinBuilder joinBuilder = JoinFactory.createNew()
.outer()
.join(left)
.with(right)
.as(ClassResource.fromQualifiedName(ctx.classres().getText()));
if (outerJoinContext.leftouterjoin() != null) {
joinBuilder.leftOuter();
} else if (outerJoinContext.rightouterjoin() != null) {
joinBuilder.rightOuter();
} else {
throw new ModelJoinParsingException("Expected either 'left' or 'right' 'outer join'");
}
ctx.keepaggregatesexpr().stream()
.map(keepStatementVisitor::visitKeepaggregatesexpr)
.forEach(joinBuilder::keep);
ctx.keepattributesexpr().stream()
.map(keepStatementVisitor::visitKeepattributesexpr)
.forEach(joinBuilder::keep);
ctx.keepexpr().stream()
.map(keepStatementVisitor::visitKeepexpr)
.forEach(joinBuilder::keep);
return joinBuilder.done();
}
}
package org.rosi_project.model_sync.model_join.representation.parser.antlr;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.rosi_project.model_sync.model_join.representation.core.AttributePath;
import org.rosi_project.model_sync.model_join.representation.core.ClassResource;
import org.rosi_project.model_sync.model_join.representation.core.RelativeAttribute;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepAggregateExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepAggregateExpression.AggregationType;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepAttributesExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepReferenceExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepSubTypeExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepSuperTypeExpression;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParsingException;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinBaseVisitor;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.AttrresContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepaggregatesexprContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepattributesexprContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepexprContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepincomingexprContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepoutgoingexprContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepsubtypeexprContext;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.KeepsupertypeexprContext;
/**
* The {@code KeepStatementVisitor} creates {@link KeepExpression} instances from a parsed {@code
* ModelJoin} file.
*
* @author Rico Bergmann
*/
public class KeepStatementVisitor extends ModelJoinBaseVisitor<KeepExpression> {
/**
* A simple enumeration of the possible keep statements for compound statements as defined in the
* {@code ModelJoin} ANTLR grammar.
*/
private enum KeepExprType {
/**
* The {@code KEEP_TYPE_EXPR} subsumes both {@code keep subtype} as well as {@code keep
* supertype} expressions.
*/
KEEP_TYPE_EXPR,
/**
* The {@code KEEP_INCOMING_EXPR} corresponds to a {@code keep incoming reference} statement.
*/
KEEP_INCOMING_EXPR,
/**
* The {@code KEEP_OUTGOING_EXPR} corresponds to a {@code keep outgoing reference} statement.
*/
KEEP_OUTGOING_EXPR
}
@Override
public KeepExpression visitKeepattributesexpr(KeepattributesexprContext ctx) {
return new KeepAttributesExpression(
ctx.attrres().stream()
.map(AttrresContext::getText)
.map(AttributePath::fromQualifiedPath)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
);
}
@Override
public KeepExpression visitKeepaggregatesexpr(KeepaggregatesexprContext ctx) {
String rawAggregationType = ctx.aggrtype().getText();
AggregationType aggregationType = AggregationType.valueOf(rawAggregationType.toUpperCase());
return new KeepAggregateExpression(
aggregationType /* SUM / MAX / MIN etc */,
RelativeAttribute.of(ctx.relattr().getText()) /* the attribute in the target relation */,
AttributePath.fromQualifiedPath(ctx.classres().getText()) /* the source attribute */,
AttributePath.fromQualifiedPath(ctx.attrres().getText()) /* name in the new ModelJoin */
);
}
@Override
public KeepExpression visitKeepexpr(KeepexprContext ctx) {
KeepExprType keepExprType = determineExpressionTypeFor(ctx);
switch (keepExprType) {
case KEEP_TYPE_EXPR:
return buildKeepTypeExpressionFor(ctx);
case KEEP_INCOMING_EXPR:
return buildKeepIncomingReferenceExpressionFor(ctx);
case KEEP_OUTGOING_EXPR:
return buildKeepOutgoingReferenceExpressionFor(ctx);
default:
throw new AssertionError(keepExprType);
}
}
/**
* Checks, which kind of expression is specified in a specific {@code KeepexprContext}.
*/
private KeepExprType determineExpressionTypeFor(KeepexprContext ctx) {
/*
* From the ANTLR grammar we know that exactly one of the IF cases has to hold
*/
if (ctx.keeptypeexpr() != null) {
return KeepExprType.KEEP_TYPE_EXPR;
} else if (ctx.keepincomingexpr() != null) {
return KeepExprType.KEEP_INCOMING_EXPR;
} else if (ctx.keepoutgoingexpr() != null) {
return KeepExprType.KEEP_OUTGOING_EXPR;
} else {
throw new ModelJoinParsingException(
"Expected either 'keep reference' or 'keep type' expression: " + ctx);
}
}
/**
* Creates the appropriate {@link KeepExpression} for a {@code KeepexprContext}.
*/
private KeepExpression buildKeepTypeExpressionFor(KeepexprContext ctx) {
if (ctx.keeptypeexpr().keepsubtypeexpr() != null) {
return buildKeepSubtypeExpressionFor(ctx);
} else if (ctx.keeptypeexpr().keepsupertypeexpr() != null) {
return buildKeepSupertypeExpressionFor(ctx);
} else {
throw new ModelJoinParsingException(
"Expected either 'keep supertype' or 'keep subtype' expression: " + ctx);
}
}
/**
* Creates the {@link KeepReferenceExpression} instance contained in a {@code KeepexprContext}.
* This method assumes that the provided {@code ctx} indeed contains a keep incoming reference
* statement.
*/
private KeepReferenceExpression buildKeepIncomingReferenceExpressionFor(KeepexprContext ctx) {
KeepincomingexprContext incomingCtx = ctx.keepincomingexpr();
AttributePath attributeToKeep = AttributePath
.fromQualifiedPath(incomingCtx.attrres().getText());
List<KeepExpression> nestedExpressions = ctx.keepattributesexpr().stream()
.map(this::visitKeepattributesexpr)
.collect(Collectors.toList());
nestedExpressions.addAll(ctx.keepaggregatesexpr().stream()
.map(this::visitKeepaggregatesexpr)
.collect(Collectors.toList())
);
nestedExpressions.addAll(ctx.keepexpr().stream()
.map(this::visitKeepexpr)
.collect(Collectors.toList())
);
KeepReferenceExpression.KeepReferenceBuilder expressionBuilder = KeepReferenceExpression.keep()
.incoming(attributeToKeep);
ClassResource targetClass;
if (incomingCtx.classres() != null) {
targetClass = ClassResource.fromQualifiedName(incomingCtx.classres().getText());
} else {
targetClass = attributeToKeep.getContainingClass();
}
expressionBuilder.as(targetClass);
nestedExpressions.forEach(expressionBuilder::keep);
return expressionBuilder.buildExpression();
}
/**
* Creates the {@link KeepReferenceExpression} instance contained in a {@code KeepexprContext}.
* This method assumes that the provided {@code ctx} indeed contains a keep outgoing reference
* statement.
*/
private KeepReferenceExpression buildKeepOutgoingReferenceExpressionFor(KeepexprContext ctx) {
KeepoutgoingexprContext outgoingCtx = ctx.keepoutgoingexpr();
AttributePath attributeToKeep = AttributePath
.fromQualifiedPath(outgoingCtx.attrres().getText());
List<KeepExpression> nestedExpressions = ctx.keepattributesexpr().stream()
.map(this::visitKeepattributesexpr)
.collect(Collectors.toList());
nestedExpressions.addAll(ctx.keepaggregatesexpr().stream()
.map(this::visitKeepaggregatesexpr)
.collect(Collectors.toList())
);
nestedExpressions.addAll(ctx.keepexpr().stream()
.map(this::visitKeepexpr)
.collect(Collectors.toList())
);
KeepReferenceExpression.KeepReferenceBuilder expressionBuilder = KeepReferenceExpression.keep()
.outgoing(attributeToKeep);
ClassResource targetClass;
if (outgoingCtx.classres() != null) {
targetClass = ClassResource.fromQualifiedName(outgoingCtx.classres().getText());
} else {
targetClass = attributeToKeep.getContainingClass();
}
expressionBuilder.as(targetClass);
nestedExpressions.forEach(expressionBuilder::keep);
return expressionBuilder.buildExpression();
}
/**
* Creates the {@link KeepSubTypeExpression} instance contained in a {@code KeepexprContext}. This
* method assumes that the provided {@code ctx} indeed contains a keep subtype statement.
*/
private KeepSubTypeExpression buildKeepSubtypeExpressionFor(KeepexprContext ctx) {
KeepsubtypeexprContext subtypeCtx = ctx.keeptypeexpr().keepsubtypeexpr();
ClassResource typeToKeep = ClassResource.fromQualifiedName(subtypeCtx.classres(0).getText());
List<KeepExpression> nestedExpressions = ctx.keepattributesexpr().stream()
.map(this::visitKeepattributesexpr)
.collect(Collectors.toList());
nestedExpressions.addAll(ctx.keepaggregatesexpr().stream()
.map(this::visitKeepaggregatesexpr)
.collect(Collectors.toList())
);
nestedExpressions.addAll(ctx.keepexpr().stream()
.map(this::visitKeepexpr)
.collect(Collectors.toList())
);
KeepSubTypeExpression.KeepSubTypeBuilder expressionBuilder = KeepSubTypeExpression.keep()
.subtype(typeToKeep);
ClassResource targetClass;
if (subtypeCtx.classres() != null) {
targetClass = ClassResource.fromQualifiedName(subtypeCtx.classres(1).getText());
} else {
targetClass = typeToKeep;
}
expressionBuilder.as(targetClass);
nestedExpressions.forEach(expressionBuilder::keep);
return expressionBuilder.buildExpression();
}
/**
* Creates the {@link KeepSuperTypeExpression} instance contained in a {@code KeepexprContext}. This
* method assumes that the provided {@code ctx} indeed contains a keep supertype statement.
*/
private KeepSuperTypeExpression buildKeepSupertypeExpressionFor(KeepexprContext ctx) {
KeepsupertypeexprContext supertypeCtx = ctx.keeptypeexpr().keepsupertypeexpr();
ClassResource typeToKeep = ClassResource.fromQualifiedName(supertypeCtx.classres(0).getText());
List<KeepExpression> nestedExpressions = ctx.keepattributesexpr().stream()
.map(this::visitKeepattributesexpr)
.collect(Collectors.toList());
nestedExpressions.addAll(ctx.keepaggregatesexpr().stream()
.map(this::visitKeepaggregatesexpr)
.collect(Collectors.toList())
);
nestedExpressions.addAll(ctx.keepexpr().stream()
.map(this::visitKeepexpr)
.collect(Collectors.toList())
);
KeepSuperTypeExpression.KeepSuperTypeBuilder expressionBuilder = KeepSuperTypeExpression.keep()
.supertype(typeToKeep);
ClassResource targetClass;
if (supertypeCtx.classres() != null) {
targetClass = ClassResource.fromQualifiedName(supertypeCtx.classres(1).getText());
} else {
targetClass = typeToKeep;
}
expressionBuilder.as(targetClass);
nestedExpressions.forEach(expressionBuilder::keep);
return expressionBuilder.buildExpression();
}
}
package org.rosi_project.model_sync.model_join.representation.parser.antlr;
import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinBaseVisitor;
import org.rosi_project.model_sync.model_join.representation.parser.antlr.generated.ModelJoinParser.ModeljoinContext;
import org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder;
/**
* The {@code ModelJoinVisitor} creates a {@link ModelJoinExpression} instances from a parsed {@code
* ModelJoin} file.
*
* @author Rico Bergmann
*/
public class ModelJoinVisitor extends ModelJoinBaseVisitor<ModelJoinExpression> {
@Override
public ModelJoinExpression visitModeljoin(ModeljoinContext ctx) {
JoinStatementVisitor joinVisitor = new JoinStatementVisitor();
ModelJoinBuilder modelJoinBuilder = ModelJoinBuilder.createNewModelJoin();
ctx.join().stream() //
.map(joinVisitor::visitJoin) //
.forEach(modelJoinBuilder::add);
return modelJoinBuilder.build();
}
}
package org.rosi_project.model_sync.model_join.representation.parser.legacy;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParser;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParsingException;
import org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder;
/**
* The {@code DefaultModelJoinParser} reads a {@code ModelJoin} file from disk and creates matching
* representation for it.
*
* @author Rico Bergmann
*/
@Deprecated
public class DefaultModelJoinParser {
/**
* Converts the {@code ModelJoin} file into a {@link ModelJoinExpression}, wrapped into an {@code
* Optional}. If parsing fails, an empty {@code Optional} will be returned.
*/
@Nonnull
public static Optional<ModelJoinExpression> read(@Nonnull File modelFile) {
DefaultModelJoinParser parser = new DefaultModelJoinParser(modelFile, ErrorReportingStrategy.OPTIONAL);
return parser.run();
}
/**
* Converts the {@code ModelJoin} file into a {@link ModelJoinExpression}. If parsing fails, a
* {@link ModelJoinParsingException} will be thrown.
*
* @throws ModelJoinParsingException if the model may not be parsed for some reason.
*/
@Nonnull
public static ModelJoinExpression readOrThrow(@Nonnull File modelFile) {
DefaultModelJoinParser parser = new DefaultModelJoinParser(modelFile, ErrorReportingStrategy.EXCEPTION);
Optional<ModelJoinExpression> result = parser.run();
if (result.isPresent()) {
return result.get();
} else {
/*
* Theoretically, the parser should throw the exception by itself, so this code should never
* actually run. It is merely here for safety purposes (unchecked access of Optional.get())
*/
throw new ModelJoinParsingException("Result not present but no exception was thrown either");
}
}
/**
* Indicates what should happen if parsing fails.
*/
enum ErrorReportingStrategy {
/**
* On failure an empty {@code Optional} should be returned.
*/
OPTIONAL,
/**
* On failure an {@link ModelJoinParsingException} should be thrown.
*/
EXCEPTION
}
private static final Pattern EMPTY_LINE = Pattern.compile("^\\s*$");
private static final Pattern IMPORT_STATEMENT = Pattern.compile("^import .*");
private static final Pattern TARGET_STATEMENT = Pattern.compile("^target .*");
private static final Pattern JOIN_STATEMENT = Pattern
.compile("^(((left|right) outer)|theta|natural) join .*");
@Nonnull
private final File modelFile;
@Nonnull
private final ErrorReportingStrategy errorReportingStrategy;
/**
* Full constructor.
*
* @param modelFile the file to parse
* @param errorReportingStrategy what to do in case an error occurs.
*/
private DefaultModelJoinParser(@Nonnull File modelFile,
@Nonnull ErrorReportingStrategy errorReportingStrategy) {
this.modelFile = modelFile;
this.errorReportingStrategy = errorReportingStrategy;
}
/**
* Creates the {@code ModelJoin} representation from the {@code modelFile}.
* <p>
* Depending on the selected {@code errorReportingStrategy} either an Exception will be thrown, or
* an empty {@code Optional} will be returned if something goes wrong.
*/
private Optional<ModelJoinExpression> run() {
ModelJoinExpression resultingModel;
try (FileInputStream modelInputStream = new FileInputStream(modelFile);
InputStreamReader modelInputReader = new InputStreamReader(modelInputStream);
BufferedReader bufferedModelReader = new BufferedReader(modelInputReader)) {
ModelJoinBuilder modelJoinBuilder = ModelJoinBuilder.createNewModelJoin();
readModelFileAndPopulateBuilder(bufferedModelReader, modelJoinBuilder);
resultingModel = modelJoinBuilder.build();
} catch (ModelJoinParsingException e) {
switch (errorReportingStrategy) {
case OPTIONAL:
return Optional.empty();
case EXCEPTION:
throw e;
default:
throw new AssertionError(errorReportingStrategy);
}
} catch (IOException e) {
switch (errorReportingStrategy) {
case OPTIONAL:
return Optional.empty();
case EXCEPTION:
throw new ModelJoinParsingException(e);
default:
throw new AssertionError(errorReportingStrategy);
}
}
return Optional.of(resultingModel);
}
/**
* Reads the {@code modelFile} and constructs the corresponding {@code ModelJoin} on the fly.
* <p>
* On error, an {@code IOException} or an {@link ModelJoinParsingException} will be thrown.
*
* @throws ModelJoinParsingException if the model file's content are malformed or another
* component failed
* @throws IOException if the model file might not be read any further
*/
private void readModelFileAndPopulateBuilder(BufferedReader modelReader,
ModelJoinBuilder modelBuilder) throws IOException {
String currentLine;
while ((currentLine = modelReader.readLine()) != null) {
if (lineShouldBeSkipped(currentLine)) {
// the line should be skipped. So we do nothing.
continue;
} else if (lineStartsJoinDeclaration(currentLine)) {
// a new join is being declared. Delegate to the join parser to handle the rest.
JoinParser joinParser = new JoinParser(currentLine, modelReader);
modelBuilder.add(joinParser.run());
} else {
// we do not know what to do with the current line. Should abort.
throw new ModelJoinParsingException("Unexpected line: '" + currentLine + "'");
}
}
}
/**
* Checks, whether the parser should consider the given {@code line} any further, or just ignore
* it.
* <p>
* A line should be skipped, iff.
* <ul>
* <li>It only consists of whitespace characters</li>
* <li>It is an {@code import statement} (as those are not yet considered in the current {@code
* ModelJoin} abstraction)</li>
* <li>It is a {@code target statement} (as those are not yet considered in the current {@code
* ModelJoin} abstraction)</li>
* </ul>
*/
private boolean lineShouldBeSkipped(String line) {
return EMPTY_LINE.matcher(line).matches() //
|| IMPORT_STATEMENT.matcher(line).matches() //
|| TARGET_STATEMENT.matcher(line).matches();
}
/**
* Checks, whether the given {@code line} is the beginning of a {@code join statement}.
*/
private boolean lineStartsJoinDeclaration(String line) {
return JOIN_STATEMENT.matcher(line).matches();
}
}
package org.rosi_project.model_sync.model_join.representation.parser; package org.rosi_project.model_sync.model_join.representation.parser.legacy;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -10,6 +10,7 @@ import org.rosi_project.model_sync.model_join.representation.core.ClassResource; ...@@ -10,6 +10,7 @@ import org.rosi_project.model_sync.model_join.representation.core.ClassResource;
import org.rosi_project.model_sync.model_join.representation.core.OCLConstraint; import org.rosi_project.model_sync.model_join.representation.core.OCLConstraint;
import org.rosi_project.model_sync.model_join.representation.grammar.JoinExpression; import org.rosi_project.model_sync.model_join.representation.grammar.JoinExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepExpression; import org.rosi_project.model_sync.model_join.representation.grammar.KeepExpression;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParsingException;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory; import org.rosi_project.model_sync.model_join.representation.util.JoinFactory;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.NaturalJoinBuilder; import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.NaturalJoinBuilder;
import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.OuterJoinBuilder; import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.OuterJoinBuilder;
...@@ -19,7 +20,7 @@ import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.Th ...@@ -19,7 +20,7 @@ import org.rosi_project.model_sync.model_join.representation.util.JoinFactory.Th
* The {@code JoinParser} takes care of reading an individual {@code join statement} and creates the * The {@code JoinParser} takes care of reading an individual {@code join statement} and creates the
* corresponding {@link JoinExpression}. * corresponding {@link JoinExpression}.
* <p> * <p>
* This class is primarily intended to be created and invoked by the {@link ModelJoinParser} only as * This class is primarily intended to be created and invoked by the {@link DefaultModelJoinParser} only as
* will modify the state of that as well. * will modify the state of that as well.
* *
* @author Rico Bergmann * @author Rico Bergmann
...@@ -50,7 +51,7 @@ class JoinParser { ...@@ -50,7 +51,7 @@ class JoinParser {
/** /**
* Full constructor. * Full constructor.
* *
* @param startingLine the line that was last read by the {@link ModelJoinParser} and should * @param startingLine the line that was last read by the {@link DefaultModelJoinParser} and should
* therefore become the starting point for {@code this} parser. * therefore become the starting point for {@code this} parser.
* @param modelReader the reader that provides access to the following lines in the {@code * @param modelReader the reader that provides access to the following lines in the {@code
* ModelJoin} file currently being read. * ModelJoin} file currently being read.
......
package org.rosi_project.model_sync.model_join.representation.parser; package org.rosi_project.model_sync.model_join.representation.parser.legacy;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -12,6 +12,7 @@ import javax.annotation.Nonnull; ...@@ -12,6 +12,7 @@ import javax.annotation.Nonnull;
import org.rosi_project.model_sync.model_join.representation.core.AttributePath; import org.rosi_project.model_sync.model_join.representation.core.AttributePath;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepAttributesExpression; import org.rosi_project.model_sync.model_join.representation.grammar.KeepAttributesExpression;
import org.rosi_project.model_sync.model_join.representation.grammar.KeepExpression; import org.rosi_project.model_sync.model_join.representation.grammar.KeepExpression;
import org.rosi_project.model_sync.model_join.representation.parser.ModelJoinParsingException;
/** /**
* The {@code KeepParser} takes care of reading the {@code keep statements} and creating the * The {@code KeepParser} takes care of reading the {@code keep statements} and creating the
......
...@@ -11,6 +11,15 @@ import javax.annotation.Nonnull; ...@@ -11,6 +11,15 @@ import javax.annotation.Nonnull;
*/ */
public class Assert { public class Assert {
/**
* Ensures that an object is not {@code null}.
*/
public static void notNull(Object obj, @Nonnull String failureMessage) {
if (obj == null) {
throw new AssertionError(failureMessage);
}
}
/** /**
* Ensures that none of the arguments is {@code null}. * Ensures that none of the arguments is {@code null}.
*/ */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment