mirror of https://github.com/eitch/first-wave.git
mim.integration initial commit.
This commit is contained in:
parent
8fa4d79ad7
commit
edb21f1319
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/.settings
|
|
@ -0,0 +1,29 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>maven.platform.parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>mim.integration</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>platform</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>mim.meta</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>parser.delphi</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,383 @@
|
|||
package ch.bitwave.mim.integration.delphi;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import ch.bitwave.lang.delphi.ast.AncestorDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.ArrayType;
|
||||
import ch.bitwave.lang.delphi.ast.ClassType;
|
||||
import ch.bitwave.lang.delphi.ast.ClassifierDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.DelphiSyntaxAdapter;
|
||||
import ch.bitwave.lang.delphi.ast.FieldDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.Identifier;
|
||||
import ch.bitwave.lang.delphi.ast.InterfaceSection;
|
||||
import ch.bitwave.lang.delphi.ast.InterfaceType;
|
||||
import ch.bitwave.lang.delphi.ast.MemberConstructorDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.MemberConstructorImplementation;
|
||||
import ch.bitwave.lang.delphi.ast.MemberDestructorDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.MemberDestructorImplementation;
|
||||
import ch.bitwave.lang.delphi.ast.MemberFunctionDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.MemberFunctionImplementation;
|
||||
import ch.bitwave.lang.delphi.ast.MemberProcedureDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.MemberProcedureImplementation;
|
||||
import ch.bitwave.lang.delphi.ast.MethodDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.Node;
|
||||
import ch.bitwave.lang.delphi.ast.QualifiedIdentifier;
|
||||
import ch.bitwave.lang.delphi.ast.Type;
|
||||
import ch.bitwave.lang.delphi.ast.TypeDeclaration;
|
||||
import ch.bitwave.lang.delphi.ast.Unit;
|
||||
import ch.bitwave.lang.delphi.ast.UsesSection;
|
||||
import ch.bitwave.lang.delphi.ast.VariableDeclaration;
|
||||
import ch.bitwave.lang.delphi.mapping.DelphiSyntaxWriter;
|
||||
import ch.bitwave.mim.m2.core.Classifier;
|
||||
import ch.bitwave.mim.m2.core.Dependent;
|
||||
import ch.bitwave.mim.m2.core.Generalization;
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
import ch.bitwave.mim.m2.core.MimType;
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
import ch.bitwave.mim.m2.core.Operation;
|
||||
import ch.bitwave.mim.m2.core.Property;
|
||||
import ch.bitwave.mim.m2.core.StubClass;
|
||||
import ch.bitwave.mim.m2.core.StubType;
|
||||
import ch.bitwave.mim.m2.core.UnknownMemberException;
|
||||
import ch.bitwave.mim.m2.profiles.DelphiProfile;
|
||||
|
||||
/**
|
||||
* Maps a Delphi AST into a Mim M2 model.
|
||||
*/
|
||||
public class DelphiASTMapper extends DelphiSyntaxAdapter {
|
||||
|
||||
// private static final Logger LOGGER =
|
||||
// Logger.getLogger(DelphiMappingVisitor.class
|
||||
// .getSimpleName());
|
||||
private Stack<MimElement> stack;
|
||||
DelphiProfile profile;
|
||||
|
||||
public DelphiASTMapper(final DelphiProfile profile, final MimPackage parent) {
|
||||
this.stack = new Stack<MimElement>();
|
||||
this.profile = profile;
|
||||
this.stack.push(parent);
|
||||
}
|
||||
|
||||
private void pop(final MimElement element) {
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
private void push(final MimElement element) {
|
||||
this.stack.push(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnit(final Unit node) {
|
||||
MimPackage parent = getParentAs(MimPackage.class);
|
||||
MimPackage unitPackage = this.profile.createPackage(node.getIdentifier().getName());
|
||||
parent.addOwnedMember(unitPackage);
|
||||
push(unitPackage);
|
||||
super.visitUnit(node);
|
||||
pop(unitPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassType(final ClassType node) {
|
||||
processClassifier(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInterfaceType(final InterfaceType node) {
|
||||
MimClass type = processClassifier(node);
|
||||
if (type != null) {
|
||||
type.addStereotype(this.profile.getInterfaceStereotype());
|
||||
}
|
||||
}
|
||||
|
||||
private MimClass processClassifier(final ClassifierDeclaration node) {
|
||||
if (!(node.getParent() instanceof TypeDeclaration)) {
|
||||
throw new RuntimeException(String.format(
|
||||
"%s is not subordinate to a type declaration.", node));
|
||||
}
|
||||
TypeDeclaration dec = (TypeDeclaration) node.getParent();
|
||||
MimPackage parent = getParentAs(MimPackage.class);
|
||||
String name = dec.getIdentifier().getName();
|
||||
MimClass type = parent.findOwnedMemberAs(name, MimClass.class);
|
||||
if (type == null) {
|
||||
type = this.profile.createClass(name);
|
||||
type.setOwner(parent);
|
||||
}
|
||||
type.setAbstract(node.isAbstract());
|
||||
parent.addOwnedMember(type);
|
||||
push(type);
|
||||
descend(node);
|
||||
pop(type);
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberConstructorDeclaration(final MemberConstructorDeclaration node) {
|
||||
declareMethod(node);
|
||||
super.visitMemberConstructorDeclaration(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberDestructorDeclaration(final MemberDestructorDeclaration node) {
|
||||
declareMethod(node);
|
||||
super.visitMemberDestructorDeclaration(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberFunctionDeclaration(final MemberFunctionDeclaration node) {
|
||||
declareMethod(node);
|
||||
super.visitMemberFunctionDeclaration(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberProcedureDeclaration(final MemberProcedureDeclaration node) {
|
||||
declareMethod(node);
|
||||
super.visitMemberProcedureDeclaration(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberConstructorImplementation(final MemberConstructorImplementation node) {
|
||||
MimPackage pack = getFirstParentAs(MimPackage.class);
|
||||
try {
|
||||
QualifiedIdentifier methodIdent = node.getIdentifier();
|
||||
MimClass parent = (MimClass) pack.getOwnedMemberByName(methodIdent.getNamespaceName());
|
||||
Operation method = (Operation) parent.getOwnedMemberByName(methodIdent.getObjectName());
|
||||
push(method);
|
||||
super.visitMemberConstructorImplementation(node);
|
||||
pop(method);
|
||||
} catch (UnknownMemberException e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Failed to map method implementation %s due to: %s", node, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberDestructorImplementation(final MemberDestructorImplementation node) {
|
||||
MimPackage pack = getFirstParentAs(MimPackage.class);
|
||||
try {
|
||||
QualifiedIdentifier methodIdent = node.getIdentifier();
|
||||
MimClass parent = (MimClass) pack.getOwnedMemberByName(methodIdent.getNamespaceName());
|
||||
Operation method = (Operation) parent.getOwnedMemberByName(methodIdent.getObjectName());
|
||||
push(method);
|
||||
super.visitMemberDestructorImplementation(node);
|
||||
pop(method);
|
||||
} catch (UnknownMemberException e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Failed to map method implementation %s due to: %s", node, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberFunctionImplementation(final MemberFunctionImplementation node) {
|
||||
MimPackage pack = getFirstParentAs(MimPackage.class);
|
||||
try {
|
||||
QualifiedIdentifier methodIdent = node.getIdentifier();
|
||||
MimClass parent = (MimClass) pack.getOwnedMemberByName(methodIdent.getNamespaceName());
|
||||
Operation method = (Operation) parent.getOwnedMemberByName(methodIdent.getObjectName());
|
||||
push(method);
|
||||
super.visitMemberFunctionImplementation(node);
|
||||
pop(method);
|
||||
} catch (UnknownMemberException e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Failed to map method implementation %s due to: %s", node, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberProcedureImplementation(final MemberProcedureImplementation node) {
|
||||
MimPackage pack = getFirstParentAs(MimPackage.class);
|
||||
try {
|
||||
QualifiedIdentifier methodIdent = node.getIdentifier();
|
||||
MimClass parent = (MimClass) pack.getOwnedMemberByName(methodIdent.getNamespaceName());
|
||||
Operation method = (Operation) parent.getOwnedMemberByName(methodIdent.getObjectName());
|
||||
push(method);
|
||||
super.visitMemberProcedureImplementation(node);
|
||||
pop(method);
|
||||
} catch (UnknownMemberException e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Failed to map method implementation %s due to: %s", node, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAncestorDeclaration(final AncestorDeclaration node) {
|
||||
MimClass parent = getParentAs(MimClass.class);
|
||||
for (Identifier ancestorId : node.getAncestors()) {
|
||||
Classifier general = resolveClassReference(parent.getPackage(), ancestorId);
|
||||
Generalization gen = new Generalization();
|
||||
if (general instanceof StubClass) {
|
||||
((StubClass) general).addReferer(gen);
|
||||
}
|
||||
gen.setGeneral(general);
|
||||
gen.setSpecific(parent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldDeclaration(final FieldDeclaration node) {
|
||||
MimClass parent = getParentAs(MimClass.class);
|
||||
VariableDeclaration var = node.getVariable();
|
||||
Type type = var.getType();
|
||||
for (Identifier ident : var.getIdentifiers()) {
|
||||
Property field = new Property();
|
||||
field.setName(ident.getName());
|
||||
field.setDefaultValue(representNode(var.getDefaultValue()));
|
||||
int upperBound = 1;
|
||||
if (type instanceof ArrayType) {
|
||||
type = ((ArrayType) type).getComponentType();
|
||||
upperBound = 0;
|
||||
}
|
||||
String typeName = representNode(type);
|
||||
MimType modelType = resolveType(typeName);
|
||||
field.setType(modelType);
|
||||
field.getMultiplicity().setUpper(upperBound);
|
||||
parent.addOwnedMember(field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the type for the given type name. The profile is
|
||||
* searched first, followed by the package members. If the type is unknown,
|
||||
* a type stub is created and registered with the package.
|
||||
*
|
||||
* @param typeName
|
||||
* the name of the type to resolve.
|
||||
* @return the resolved type.
|
||||
*/
|
||||
@Nonnull
|
||||
private MimType resolveType(final String typeName) {
|
||||
MimType result = this.profile.findType(typeName);
|
||||
if (result == null) {
|
||||
MimPackage parent = getFirstParentAs(MimPackage.class);
|
||||
result = parent.findType(typeName);
|
||||
if (result == null) {
|
||||
createStubType(parent, typeName, String.format(
|
||||
"Type %s is neither provided by the profile nor known to package %s.",
|
||||
typeName, parent.getName()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInterfaceSection(final InterfaceSection node) {
|
||||
UsesSection usesSection = node.getUsesSection();
|
||||
if (usesSection != null) {
|
||||
MimPackage parent = getParentAs(MimPackage.class);
|
||||
for (Identifier importedIdentifier : usesSection.getImports()) {
|
||||
parent.addPackageImportStub(importedIdentifier.getName());
|
||||
}
|
||||
}
|
||||
super.visitInterfaceSection(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIdentifier(final Identifier node) {
|
||||
Dependent parent = findFirstParentAs(Dependent.class);
|
||||
if (parent != null) {
|
||||
parent.addIdentifierDependency(node.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private String representNode(final Node node) {
|
||||
if (node == null) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return DelphiSyntaxWriter.writeToString(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the class with the given identifier within the given parent
|
||||
* package. Creates a stub class in case the class is not known.
|
||||
*
|
||||
* @param parent
|
||||
* @param identifier
|
||||
* @return
|
||||
*/
|
||||
private Classifier resolveClassReference(final MimPackage parent, final Identifier identifier) {
|
||||
String name = identifier.getName();
|
||||
try {
|
||||
NamedElement general = parent.getMemberByName(name);
|
||||
if (!(general instanceof Classifier)) {
|
||||
String message = String.format("Class %s generalizes non-classifier \"%s\".",
|
||||
general.getName(), name);
|
||||
// LOGGER.warning(message);
|
||||
general = createStubClass(parent, identifier, message);
|
||||
}
|
||||
return (Classifier) general;
|
||||
} catch (UnknownMemberException e) {
|
||||
String message = String.format(
|
||||
"Class %s generalizes \"%s\", which is not known to package \"%s\".",
|
||||
parent.getName(), name, parent.getName());
|
||||
// LOGGER.warning(message);
|
||||
return createStubClass(parent, identifier, message);
|
||||
}
|
||||
}
|
||||
|
||||
private Classifier createStubClass(final MimPackage parent, final Identifier identifier,
|
||||
final String message) {
|
||||
StubClass stub = new StubClass();
|
||||
stub.setName(identifier.getName());
|
||||
stub.setStubReason(message);
|
||||
parent.addOwnedMember(stub);
|
||||
return stub;
|
||||
}
|
||||
|
||||
private MimType createStubType(final MimPackage parent, final String typeName,
|
||||
final String format) {
|
||||
StubType stub = new StubType();
|
||||
stub.setName(typeName);
|
||||
parent.addOwnedMember(stub);
|
||||
return stub;
|
||||
}
|
||||
|
||||
private void declareMethod(final MethodDeclaration node) {
|
||||
Classifier parent = getParentAs(Classifier.class);
|
||||
Operation op = new Operation();
|
||||
op.setName(node.getIdentifier().getName());
|
||||
parent.addOwnedMember(op);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getParentAs(final java.lang.Class<T> elementClass) {
|
||||
if (this.stack.isEmpty()) {
|
||||
throw new RuntimeException(
|
||||
"Cannot return the current parent because the element stack is empty.");
|
||||
}
|
||||
return (T) this.stack.get(this.stack.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascends the element stack until an element of the given type is found.
|
||||
*
|
||||
* @param elementClass
|
||||
* the type to retrieve from the stack.
|
||||
* @return
|
||||
*/
|
||||
private <T> T getFirstParentAs(final Class<T> elementClass) {
|
||||
T result = findFirstParentAs(elementClass);
|
||||
if (result == null) {
|
||||
throw new RuntimeException(String.format(
|
||||
"There is no element of type %s present on the stack.",
|
||||
elementClass.getSimpleName()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T findFirstParentAs(final Class<T> elementClass) {
|
||||
for (int i = this.stack.size() - 1; i >= 0; i--) {
|
||||
MimElement cur = this.stack.get(i);
|
||||
if (elementClass.isAssignableFrom(cur.getClass())) {
|
||||
return (T) cur;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package ch.bitwave.mim.integration.delphi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import ch.bitwave.lang.delphi.ast.Unit;
|
||||
import ch.bitwave.lang.delphi.driver.DelphiSourceParserDriver;
|
||||
import ch.bitwave.mim.integration.toolbox.FileSystem;
|
||||
import ch.bitwave.mim.m2.core.Classifier;
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
import ch.bitwave.mim.m2.core.PackageImport;
|
||||
import ch.bitwave.mim.m2.core.StubClass;
|
||||
import ch.bitwave.mim.m2.core.StubPackage;
|
||||
import ch.bitwave.mim.m2.profiles.DelphiProfile;
|
||||
|
||||
/**
|
||||
* Integrates a Delphi AST into the mim workspace by transforming it into m2
|
||||
* elements.
|
||||
*/
|
||||
public class DelphiIntegrator {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(DelphiIntegrator.class.getSimpleName());
|
||||
private File rootFolder;
|
||||
|
||||
public File getRootFolder() {
|
||||
return this.rootFolder;
|
||||
}
|
||||
|
||||
public void setRootFolder(final File rootFolder) {
|
||||
this.rootFolder = rootFolder;
|
||||
}
|
||||
|
||||
public DelphiIntegrator(final File rootFolder) {
|
||||
this.rootFolder = rootFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Integrates the given unit below the given target namespace. The package
|
||||
* hierarchy is derived from the fully qualified path name stored in the
|
||||
* unit as expressed relative to the absolute root folder specified for the
|
||||
* integrator.
|
||||
*
|
||||
* @param targetPackage
|
||||
* @param unit
|
||||
*/
|
||||
public void integrate(final DelphiProfile profile, final MimPackage targetPackage,
|
||||
final Unit unit) {
|
||||
MimPackage parent = ensurePackageHierarchy(targetPackage, unit.getSourceLocation());
|
||||
DelphiASTMapper visitor = new DelphiASTMapper(profile, parent);
|
||||
unit.accept(visitor);
|
||||
}
|
||||
|
||||
public void integrateFolder(final DelphiProfile profile, final MimPackage targetPackage,
|
||||
final File folder, final boolean recursive, final String encoding) throws IOException {
|
||||
LOGGER.info(String.format("Integrating folder %s.", folder.getAbsolutePath()));
|
||||
Collection<File> found = FileUtils.listFiles(folder, new String[] { "pas" }, recursive);
|
||||
for (File file : found) {
|
||||
integrateFile(profile, targetPackage, file, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public void integrateFile(final DelphiProfile profile, final MimPackage targetPackage,
|
||||
final File file, final String encoding) throws IOException {
|
||||
LOGGER.info(String.format("Integrating file %s.", file.getAbsolutePath()));
|
||||
Unit unit = new DelphiSourceParserDriver().readFile(file, encoding);
|
||||
unit.setSourceLocation(file);
|
||||
integrate(profile, targetPackage, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to replace all Stub classes with the actual classes they
|
||||
* represent.
|
||||
*/
|
||||
public void resolveStubs(final MimPackage rootPackage) {
|
||||
resolvePackageStubs(rootPackage, rootPackage);
|
||||
resolveClassStubs(rootPackage);
|
||||
}
|
||||
|
||||
private void resolvePackageStubs(final MimPackage rootPackage, final MimPackage subject) {
|
||||
List<PackageImport> imports = subject.getPackageImports();
|
||||
Map<String, MimPackage> nonStubs = rootPackage.getAllOwnedNonStubPackages(true);
|
||||
for (PackageImport pimp : imports) {
|
||||
MimPackage target = pimp.getImportedPackage();
|
||||
if (target.isStub()) {
|
||||
StubPackage stub = (StubPackage) target;
|
||||
MimPackage resolved = nonStubs.get(stub.getName().toLowerCase());
|
||||
if (resolved != null) {
|
||||
stub.replaceWith(resolved);
|
||||
LOGGER.info(String.format("Replaced %s with %s.", stub, resolved));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (MimPackage subPackage : subject.getNestedPackages()) {
|
||||
resolvePackageStubs(rootPackage, subPackage);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveClassStubs(final MimPackage rootPackage) {
|
||||
List<StubClass> stubs = rootPackage.getOwnedMembersOfType(StubClass.class);
|
||||
Map<String, MimClass> nonStubs = rootPackage.getAllNonStubClasses(true);
|
||||
for (StubClass stub : stubs) {
|
||||
Classifier resolved = nonStubs.get(stub.getName().toLowerCase());
|
||||
if (resolved instanceof MimClass) {
|
||||
stub.replaceWith((MimClass) resolved);
|
||||
}
|
||||
}
|
||||
for (MimPackage subPackage : rootPackage.getNestedPackages()) {
|
||||
resolveClassStubs(subPackage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the effective package in which to place the types introduced in
|
||||
* the given unit, based on the given source location of the unit in
|
||||
* relation to the integrator's root folder. Existing packages are reused.
|
||||
* </p>
|
||||
*
|
||||
* Example: If the integrator root is "C:/source/" and the unit location is
|
||||
* "C:/source/magic/mushroom/dust.pas" then the resulting package will be
|
||||
* root.magic.mushroom.
|
||||
*
|
||||
* @param rootPackage
|
||||
* the package below which to add detail packages.
|
||||
* @param sourceLocation
|
||||
* the location of the unit, including the unit file name.
|
||||
* @return
|
||||
*/
|
||||
protected MimPackage ensurePackageHierarchy(@Nonnull final MimPackage rootPackage,
|
||||
@Nonnull final File sourceLocation) {
|
||||
File sourceFolder = sourceLocation.getParentFile();
|
||||
String[] packageNames = FileSystem.getRelativeFolders(this.rootFolder, sourceFolder);
|
||||
if (packageNames.length == 0) {
|
||||
return rootPackage;
|
||||
}
|
||||
MimPackage result = rootPackage;
|
||||
for (String packageName : packageNames) {
|
||||
result = ensureDetailPackage(result, packageName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private MimPackage ensureDetailPackage(final MimPackage parent, final String packageName) {
|
||||
MimPackage detailMember = parent.findOwnedMemberAs(packageName, MimPackage.class);
|
||||
if (detailMember == null) {
|
||||
detailMember = new MimPackage();
|
||||
detailMember.setName(packageName);
|
||||
parent.addOwnedMember(detailMember);
|
||||
}
|
||||
return detailMember;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package ch.bitwave.mim.integration.toolbox;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FileSystem {
|
||||
|
||||
public static String[] getRelativeFolders(final File root, final File detail) {
|
||||
String rootPath = root.getAbsolutePath();
|
||||
String detailPath = detail.getAbsolutePath();
|
||||
if (detailPath.length() < rootPath.length()
|
||||
|| !detailPath.toLowerCase().startsWith(rootPath.toLowerCase())) {
|
||||
throw new RuntimeException(String.format("Path \"%s\" does not root in \"%s\".",
|
||||
detailPath, rootPath));
|
||||
}
|
||||
String relativePath = detailPath.substring(rootPath.length());
|
||||
if (relativePath.startsWith(File.separator)) {
|
||||
relativePath = relativePath.substring(1);
|
||||
}
|
||||
if (relativePath.isEmpty()) {
|
||||
return new String[0];
|
||||
}
|
||||
return relativePath.split("[\\\\/]");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package ch.bitwave.mim.integration.delphi;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ch.bitwave.lang.delphi.ast.Unit;
|
||||
import ch.bitwave.lang.delphi.driver.DelphiSourceParserDriver;
|
||||
import ch.bitwave.mim.integration.testbase.Resources;
|
||||
import ch.bitwave.mim.m2.core.Classifier;
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
import ch.bitwave.mim.m2.core.Operation;
|
||||
import ch.bitwave.mim.m2.core.UnknownMemberException;
|
||||
import ch.bitwave.mim.m2.profiles.DelphiProfile;
|
||||
|
||||
/**
|
||||
* Tests the integration of Delphi sources into the metamodel.
|
||||
*/
|
||||
public class DelphiIntegratorTest {
|
||||
|
||||
@Test
|
||||
public void shouldMapPhoenixInterfaces() throws IOException, UnknownMemberException {
|
||||
DelphiIntegrator integrator = createIntegrator();
|
||||
DelphiProfile profile = new DelphiProfile();
|
||||
MimPackage rootPackage = new MimPackage();
|
||||
rootPackage.setName("root");
|
||||
integrateUnit(integrator, "product/common/", "SampleUnit7.pas", profile, rootPackage);
|
||||
MimPackage phoenixPackage = (MimPackage) rootPackage.getOwnedMemberByName("product");
|
||||
MimPackage sharedPackage = (MimPackage) phoenixPackage.getOwnedMemberByName("common");
|
||||
MimPackage pxiPackage = (MimPackage) sharedPackage.getOwnedMemberByName("SampleUnit7");
|
||||
assertEquals(5, pxiPackage.getOwnedMembersOfType(MimClass.class).size());
|
||||
MimClass clientLanguage = (MimClass) pxiPackage.getOwnedMemberByName("IClientLanguage");
|
||||
assertEquals("IClientLanguage", clientLanguage.getName());
|
||||
assertEquals(pxiPackage, clientLanguage.getNamespace());
|
||||
MimClass person = (MimClass) pxiPackage.getMemberByName("IPerson");
|
||||
assertEquals(1, person.getGenerals().size());
|
||||
Classifier general = person.getGenerals().get(0);
|
||||
assertEquals("IHuman", general.getName());
|
||||
assertTrue(general.isStereotype(profile.getInterfaceStereotype()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldResolveStubClasses() throws IOException, UnknownMemberException {
|
||||
DelphiIntegrator integrator = createIntegrator();
|
||||
DelphiProfile profile = new DelphiProfile();
|
||||
MimPackage rootPackage = new MimPackage();
|
||||
rootPackage.setName("root");
|
||||
integrateUnit(integrator, "product/common/", "SampleUnit7.pas", profile, rootPackage);
|
||||
integrateUnit(integrator, "independent/toolbox/", "SampleUnit3.pas", profile, rootPackage);
|
||||
integrator.resolveStubs(rootPackage);
|
||||
MimPackage tbi = rootPackage.findContainedPackage("SampleUnit3");
|
||||
MimClass stordef = (MimClass) tbi.getOwnedMemberByName("IStorableDefinition");
|
||||
MimPackage pxi = rootPackage.findContainedPackage("SampleUnit7");
|
||||
MimClass dawg = (MimClass) pxi.getOwnedMemberByName("IDogDefinition");
|
||||
assertTrue(dawg.isSpecializationOf(stordef));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapImplementationIdentifierDependencies() throws IOException,
|
||||
UnknownMemberException {
|
||||
DelphiIntegrator integrator = createIntegrator();
|
||||
DelphiProfile profile = new DelphiProfile();
|
||||
MimPackage rootPackage = new MimPackage();
|
||||
rootPackage.setName("root");
|
||||
integrateUnit(integrator, "varia", "Planets.pas", profile, rootPackage);
|
||||
MimPackage planets = rootPackage.findContainedPackage("Varia").findContainedPackage(
|
||||
"Planets");
|
||||
MimClass mercury = (MimClass) planets.getOwnedMemberByName("TMercury");
|
||||
assertFalse(mercury.depdendsOnIdentifier("cstrGUID_Venus"));
|
||||
Operation method = (Operation) mercury.getOwnedMemberByName("DoThing");
|
||||
assertTrue(method.depdendsOnIdentifier("cstrGUID_Venus"));
|
||||
|
||||
}
|
||||
|
||||
private DelphiIntegrator createIntegrator() {
|
||||
// Let's fake a relative location of the unit in subfolders
|
||||
// phoenix/shared to force creating of the respective package tree.
|
||||
String rootPath = "D:/delphi-source/";
|
||||
File rootFolder = new File(rootPath);
|
||||
DelphiIntegrator integrator = new DelphiIntegrator(rootFolder);
|
||||
return integrator;
|
||||
}
|
||||
|
||||
private void integrateUnit(final DelphiIntegrator integrator, final String relativePath,
|
||||
final String fileName, final DelphiProfile profile, final MimPackage rootPackage)
|
||||
throws IOException {
|
||||
File file = new File(Resources.RESOURCE_FOLDER + fileName);
|
||||
File unitLocation = new File(new File(integrator.getRootFolder(), relativePath), fileName);
|
||||
Unit unit = new DelphiSourceParserDriver().readFile(file, Resources.ENCODING);
|
||||
// System.out.print(DelphiASTPrinter.writeToString(unit));
|
||||
unit.setSourceLocation(unitLocation);
|
||||
integrator.integrate(profile, rootPackage, unit);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package ch.bitwave.mim.integration.testbase;
|
||||
|
||||
public class Resources {
|
||||
public static final String RESOURCE_FOLDER = "src/test/resources/ch/bitwave/mim/integration/delphi/";
|
||||
public static final String ENCODING = "ISO-8859-1";
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package ch.bitwave.mim.integration.toolbox;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
public class FileSystemTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void shouldGetRelativeFolders() {
|
||||
String[] folders = FileSystem.getRelativeFolders(new File("c:/root"), new File(
|
||||
"c:/root/alpha/beta"));
|
||||
assertTrue(Arrays.deepEquals(new String[] { "alpha", "beta" }, folders));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetEmptyListForIdenticalPaths() {
|
||||
String[] folders = FileSystem.getRelativeFolders(new File("c:/root"), new File("c:/ROOT"));
|
||||
assertTrue(folders.length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExceptOnGetRelativeFoldersWithIncompatiblePaths() {
|
||||
this.thrown.expect(RuntimeException.class);
|
||||
this.thrown.expectMessage("Path \"c:\\root\\alpha\\beta\" does not root in \"c:\\bar\".");
|
||||
FileSystem.getRelativeFolders(new File("c:/bar"), new File("c:/root/alpha/beta"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
unit Planets;
|
||||
|
||||
interface
|
||||
|
||||
type
|
||||
|
||||
TMercury = class
|
||||
public
|
||||
procedure DoThing;
|
||||
end;
|
||||
|
||||
TVenus = class
|
||||
public
|
||||
procedure DoThing;
|
||||
end;
|
||||
|
||||
TEarth = class
|
||||
public
|
||||
procedure DoThing;
|
||||
end;
|
||||
|
||||
TMars = class
|
||||
public
|
||||
procedure DoThing;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
procedure TMercury.DoThing;
|
||||
begin
|
||||
SomeFunction(cstrGUID_Venus);
|
||||
end;
|
||||
|
||||
procedure TVenus.DoThing;
|
||||
begin
|
||||
SomeFunction(cstrGUID_Earth);
|
||||
SomeFunction(cstrGUID_Mars);
|
||||
end;
|
||||
|
||||
procedure TEarth.DoThing;
|
||||
begin
|
||||
SomeFunction(cstrGUID_Mars);
|
||||
end;
|
||||
|
||||
procedure TMars.DoThing;
|
||||
begin
|
||||
SomeFunction(cstrGUID_Jupiter);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,83 @@
|
|||
{*
|
||||
*}
|
||||
unit SampleUnit7;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes,
|
||||
Graphics,
|
||||
Windows,
|
||||
Forms,
|
||||
SampleUnit3,
|
||||
SysUtils;
|
||||
|
||||
const
|
||||
cstrGUID_IHuman = '{82F52648-B94D-495C-B0A6-8BD4BF904846}';
|
||||
cstrGUID_IClientLanguage = '{6EA63F22-6809-4E85-960C-154421491506}';
|
||||
cstrGUID_IPerson = '{06B54C9E-6C85-4075-BDED-85406083648D}';
|
||||
cstrGUID_IDogDefinition = '{364A99BE-C420-4CA1-AA0C-0CA9C0568B59}';
|
||||
|
||||
type
|
||||
|
||||
IHuman = interface
|
||||
[cstrGUID_IHuman]
|
||||
function FormatProperties(const strProperties: string = '<titeltext> <vorname> <name>'; const
|
||||
blnRespectLanguageOrder: boolean = false): string;
|
||||
function GetAnrede: integer;
|
||||
function GetAnredeText: string;
|
||||
function GetExists: boolean;
|
||||
function GetName: string;
|
||||
function GetPropertyCount: integer;
|
||||
function GetPropertyValue(const strName: string): string;
|
||||
function GetTitel: integer;
|
||||
function GetTitelText: string;
|
||||
function GetVorname: string;
|
||||
function GetUID: integer;
|
||||
function GetAsText: string;
|
||||
procedure SetFromText(const strText: string);
|
||||
procedure SetPropertyValue(const strName, strValue: string);
|
||||
end;
|
||||
|
||||
IPerson = interface(IHuman)
|
||||
[cstrGUID_IPerson]
|
||||
function GetGroupID: integer;
|
||||
function GetKSTelle: string;
|
||||
function GetPersID: integer;
|
||||
function GetVisum: string;
|
||||
function GetID: integer;
|
||||
procedure SetID(const intID: integer);
|
||||
procedure SetTitleCode(const intTitleCode: integer);
|
||||
procedure SetFirstname(const strFirstName: string);
|
||||
procedure SetLastname(const strLastName: string);
|
||||
procedure SetGroupID(const intID: integer);
|
||||
end;
|
||||
|
||||
IClientLanguage = interface
|
||||
[cstrGUID_IClientLanguage]
|
||||
function GetAvailable: boolean;
|
||||
function GetIsSystemLanguage(out language: ISystemLanguage): boolean;
|
||||
function GetCode: string;
|
||||
function GetLabel: string;
|
||||
function GetRegionCode: integer;
|
||||
end;
|
||||
|
||||
IDogDefinition = interface(IStorableDefinition)
|
||||
[cstrGUID_IDogDefinition]
|
||||
function CreateDogDefinitionField: IDogDefinitionField;
|
||||
function GetDogDefinitionField(const index: integer): IDogDefinitionField;
|
||||
function GetDogDefinitionFieldCount: integer;
|
||||
function GetDogDefinitionFieldIndex(const strName: string): integer;
|
||||
function GetGroupField: string;
|
||||
function GetName: string;
|
||||
procedure AddDogDefinitionField(const aField: IDogDefinitionField);
|
||||
procedure RemoveDogDefinitionField(const aField: IDogDefinitionField); overload;
|
||||
procedure RemoveDogDefinitionField(const index: integer); overload;
|
||||
procedure SetGroupField(const strValue: string);
|
||||
procedure SetName(const strValue: string);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
end.
|
||||
|
Loading…
Reference in New Issue