Skip to content

Commit

Permalink
#68: Merge branch 'dev_javaplugin'
Browse files Browse the repository at this point in the history
Conflicts:
	cobigen/cobigen-javaplugin/pom.xml
  • Loading branch information
maybeec committed Dec 9, 2014
2 parents adfda5d + 2d5399e commit c681d42
Show file tree
Hide file tree
Showing 70 changed files with 1,064 additions and 298 deletions.
57 changes: 32 additions & 25 deletions cobigen/cobigen-javaplugin/pom.xml
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.capgemini</groupId>
<artifactId>cobigen-javaplugin</artifactId>
<name>CobiGen - Java Plug-in</name>
<version>1.1.1</version>
<packaging>jar</packaging>
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.capgemini</groupId>
<artifactId>cobigen-javaplugin</artifactId>
<name>CobiGen - Java Plug-in</name>
<version>1.2.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>com.capgemini</groupId>
<artifactId>cobigen-parent</artifactId>
<version>1.1.0</version>
</parent>
<parent>
<groupId>com.capgemini</groupId>
<artifactId>cobigen-parent</artifactId>
<version>1.1.1</version>
</parent>

<dependencies>
<dependency>
<groupId>com.capgemini</groupId>
<artifactId>cobigen-core</artifactId>
<version>1.1.0</version>
</dependency>
<dependencies>
<dependency>
<groupId>com.capgemini</groupId>
<artifactId>cobigen-core</artifactId>
<version>1.1.0</version>
</dependency>

<!-- QDox for Java parsing-->
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>2.0-M2</version>
</dependency>
</dependencies>
<!-- QDox for Java parsing -->
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>2.0-M2</version>
</dependency>

<!-- for advanced introspection-->
<dependency>
<groupId>net.sf.m-m-m</groupId>
<artifactId>mmm-util-core</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -18,6 +21,9 @@
import com.capgemini.cobigen.javaplugin.merger.libextension.ModifyableClassLibraryBuilder;
import com.capgemini.cobigen.javaplugin.util.freemarkerutil.IsAbstractMethod;
import com.capgemini.cobigen.javaplugin.util.freemarkerutil.IsSubtypeOfMethod;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.thoughtworks.qdox.library.ClassLibraryBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaSource;
Expand Down Expand Up @@ -70,6 +76,7 @@ public boolean isValidInput(Object input) {
*
* @author mbrunnli (15.10.2013)
*/
@SuppressWarnings("unchecked")
@Override
public Map<String, Object> createModel(Object o) {

Expand All @@ -80,14 +87,17 @@ public Map<String, Object> createModel(Object o) {
return new ParsedJavaModelBuilder().createModel((JavaClass) o);
}
if (o instanceof Object[] && isValidInput(o)) {
Map<String, Object> model;
Object[] inputArr = (Object[]) o;
Object parsedModel;
Object reflectionModel;
if (inputArr[0] instanceof JavaClass) {
model = new ParsedJavaModelBuilder().createModel((JavaClass) inputArr[0]);
parsedModel = new ParsedJavaModelBuilder().createModel((JavaClass) inputArr[0]);
reflectionModel = new ReflectedJavaModelBuilder().createModel((Class<?>) inputArr[1]);
} else {
model = new ParsedJavaModelBuilder().createModel((JavaClass) inputArr[1]);
parsedModel = new ParsedJavaModelBuilder().createModel((JavaClass) inputArr[1]);
reflectionModel = new ReflectedJavaModelBuilder().createModel((Class<?>) inputArr[0]);
}
return model;
return (Map<String, Object>) mergeModelsRecursively(parsedModel, reflectionModel);
}
return null;
}
Expand Down Expand Up @@ -216,4 +226,141 @@ public Map<String, Object> getTemplateMethods(Object input) {
methodMap.put("isSubtypeOf", new IsSubtypeOfMethod(classloader));
return methodMap;
}

/**
* Merges two models recursively. The current implementation only merges Lists and Maps recursively.
* Structures will be merged as follows:<br>
* <table>
* <tr>
* <td>Maps:</td>
* <td>equal map entries will be discovered and the values will be merged recursively</td>
* </tr>
* <tr>
* <td>Lists:</td>
* <td>Lists will only be handled if their elements are {@link Map Maps}. If so, the {@link Map Maps} will
* be compared due to their {@link ModelConstant#NAME} value. If equal, the elements will be recursively
* merged.</td>
* </tr>
* </table>
*
* @param parsedModel
* model created by parsing to be merged and preferred in case of conflicts
* @param reflectionModel
* model created by reflection to be merged
* @return the merged model. Due to implementation restrictions a {@link Map} of {@link String} to
* {@link Object}
* @author mbrunnli (17.11.2014)
*/
@SuppressWarnings("unchecked")
private Object mergeModelsRecursively(Object parsedModel, Object reflectionModel) {

if (parsedModel == null && reflectionModel == null) {
return null;
} else if (parsedModel == null) {
return reflectionModel;
} else if (reflectionModel == null) {
return parsedModel;
} else if (parsedModel.equals(reflectionModel)) {
return parsedModel;
}

if (parsedModel.getClass().equals(reflectionModel.getClass())) {
if (parsedModel instanceof Map && reflectionModel instanceof Map) {
Map<String, Object> mergedModel = Maps.newHashMap();
Map<String, Object> model1Map = (Map<String, Object>) parsedModel;
Map<String, Object> model2Map = (Map<String, Object>) reflectionModel;

Set<String> union = Sets.newHashSet(model1Map.keySet());
union.addAll(model2Map.keySet());
for (String unionKey : union) {
if (model1Map.containsKey(unionKey) && model2Map.containsKey(unionKey)) {
// Recursively merge equal keys
mergedModel.put(unionKey,
mergeModelsRecursively(model1Map.get(unionKey), model2Map.get(unionKey)));
} else if (model1Map.containsKey(unionKey)) {
mergedModel.put(unionKey, model1Map.get(unionKey));
} else {
mergedModel.put(unionKey, model2Map.get(unionKey));
}
}
return mergedModel;
}
// Case: List<Map<String, Object>> available in attributes and methods
else if (parsedModel instanceof List && reflectionModel instanceof List) {
if (!((List<?>) parsedModel).isEmpty() && ((List<?>) parsedModel).get(0) instanceof Map
|| !((List<?>) reflectionModel).isEmpty()
&& ((List<?>) reflectionModel).get(0) instanceof Map) {
List<Map<String, Object>> model1List =
Lists.newLinkedList((List<Map<String, Object>>) parsedModel);
List<Map<String, Object>> model2List =
Lists.newLinkedList((List<Map<String, Object>>) reflectionModel);
List<Object> mergedModel = Lists.newLinkedList();

// recursively merge list entries. Match them by name attribute. This is currently valid
// and might be adapted if there are greater model changes in future
Iterator<Map<String, Object>> model1ListIt = model1List.iterator();
while (model1ListIt.hasNext()) {
Map<String, Object> model1Entry = model1ListIt.next();
Iterator<Map<String, Object>> model2ListIt = model2List.iterator();
while (model2ListIt.hasNext()) {
Map<String, Object> model2Entry = model2ListIt.next();
// valid merging for fields and methods
if (model1Entry.get(ModelConstant.NAME) != null) {
if (model1Entry.get(ModelConstant.NAME).equals(
model2Entry.get(ModelConstant.NAME))) {
mergedModel.add(mergeModelsRecursively(model1Entry, model2Entry));

// remove both entries as they have been matched and recursively merged
model1ListIt.remove();
model2ListIt.remove();
break;
}
} else
// this is the case for merging recursive annotation arrays
if (model1Entry.size() == 1 && model2Entry.size() == 1) {
mergeModelsRecursively(
model1Entry.get(model1Entry.keySet().iterator().next()),
model2Entry.get(model2Entry.keySet().iterator().next()));
} else {
throw new IllegalStateException(
"Anything unintended happened. Please state an issue at GitHub or mail one of the developers");
}
}
}

// append not matched entries from list1 and list2
mergedModel.addAll(model1List);
mergedModel.addAll(model2List);
return mergedModel;
}
// we will prefer parsed model if the values of the parsed result list are of type String.
// This is the case for annotation values. QDox will always return the expression,
// which is a assigned to the annotation's value, as a string.
else if (!((List<?>) parsedModel).isEmpty()
&& ((List<?>) parsedModel).get(0) instanceof String) {
return parsedModel;
} else {
if (reflectionModel instanceof Object[]) {
return Lists.newLinkedList(Arrays.asList(reflectionModel));
} else {
return reflectionModel;
}
}
} else {
// any other type might not be merged. As the values are not equal, this might be a conflict,
// so take model1 as documented
return parsedModel;
}
} else
// we will prefer parsed model if parsed value of type String. This is the case for annotation values.
// QDox will always return the expression, which is a assigned to the annotation's value, as a string.
if (parsedModel instanceof String) {
return parsedModel;
} else if (parsedModel instanceof String[]) {
return Lists.newLinkedList(Arrays.asList(parsedModel));
} else {
throw new IllegalStateException(
"Anything unintended happened. Please state an issue at GitHub or mail one of the developers");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,20 @@ public class ModelConstant {
*/
public static final String CANONICAL_TYPE = "canonicalType";

/**
* @see #FIELDS
* @deprecated this is the deprecated accessor for the list of Java fields named 'attributes'. Please use
* {@link #FIELDS} for new features. This accessor value for the model will be removed at the
* next major release.
*/
@Deprecated
public static final String FIELDS_DEPRECATED = "attributes";

/**
* A list of all fields, whereas one field will be represented by a set of attribute mappings (
* {@link List}&lt; {@link Map}&lt;{@link String}, {@link Object}&gt;&gt;)
*/
public static final String FIELDS = "attributes";
public static final String FIELDS = "fields";

/**
* A list of all methods, whereas one method will be represented by a set of attribute mappings (
Expand All @@ -83,4 +92,9 @@ public class ModelConstant {
* JavaDoc of a method or a field ({@link String})
*/
public static final String JAVADOC = "javaDoc";

/**
* A list of all visible fields accessible via setter and getter methods including inherited fields.
*/
public static final String METHOD_ACCESSIBLE_FIELDS = "methodAccessibleFields";
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.capgemini.cobigen.javaplugin.inputreader;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
Expand Down Expand Up @@ -64,10 +63,11 @@ Map<String, Object> createModel(final JavaClass javaClass) {
extractAnnotationsRecursively(annotations, javaClass.getAnnotations());
pojoModel.put(ModelConstant.ANNOTATIONS, annotations);

List<Map<String, Object>> attributes = extractAttributes(javaClass);
pojoModel.put(ModelConstant.FIELDS, attributes);
determinePojoIds(javaClass, attributes);
collectAnnotations(javaClass, attributes);
List<Map<String, Object>> fields = extractFields(javaClass);
pojoModel.put(ModelConstant.FIELDS_DEPRECATED, fields);
pojoModel.put(ModelConstant.FIELDS, fields);
determinePojoIds(javaClass, fields);
collectAnnotations(javaClass, fields);

Map<String, Object> superclass = extractSuperclass(javaClass);
pojoModel.put(ModelConstant.EXTENDED_TYPE, superclass);
Expand Down Expand Up @@ -115,20 +115,20 @@ private List<Map<String, Object>> extractMethods(JavaClass javaClass) {
* {@link String} key to the corresponding {@link String} value of meta information
* @author mbrunnli (06.02.2013)
*/
private List<Map<String, Object>> extractAttributes(JavaClass pojo) {
private List<Map<String, Object>> extractFields(JavaClass pojo) {

List<Map<String, Object>> attributes = new LinkedList<>();
List<Map<String, Object>> fields = new LinkedList<>();
for (JavaField f : pojo.getFields()) {
if (f.isStatic()) {
continue;
}
Map<String, Object> attrValues = new HashMap<>();
attrValues.put(ModelConstant.NAME, f.getName());
attrValues.put(ModelConstant.TYPE, f.getType().getGenericValue());
attrValues.put(ModelConstant.CANONICAL_TYPE, f.getType().getGenericCanonicalName());
attributes.add(attrValues);
Map<String, Object> fieldValues = new HashMap<>();
fieldValues.put(ModelConstant.NAME, f.getName());
fieldValues.put(ModelConstant.TYPE, f.getType().getGenericValue());
fieldValues.put(ModelConstant.CANONICAL_TYPE, f.getType().getGenericCanonicalName());
fields.add(fieldValues);
}
return attributes;
return fields;
}

/**
Expand Down Expand Up @@ -234,6 +234,7 @@ private void collectAnnotations(JavaClass javaClass, List<Map<String, Object>> a
* to be analysed
* @author mbrunnli (01.04.2014)
*/
@SuppressWarnings("unchecked")
private void extractAnnotationsRecursively(Map<String, Object> annotationsMap,
List<JavaAnnotation> annotations) {

Expand All @@ -243,23 +244,30 @@ private void extractAnnotationsRecursively(Map<String, Object> annotationsMap,
annotationParameters);

for (String propertyName : annotation.getPropertyMap().keySet()) {
Object value = annotation.getPropertyMap().get(propertyName).getParameterValue();
if (value instanceof JavaAnnotation[]) {
Map<String, Object> annotationParameterParameters = new HashMap<>();
annotationParameters.put(propertyName, annotationParameterParameters);
extractAnnotationsRecursively(annotationParameterParameters,
Arrays.asList((JavaAnnotation[]) value));
Object value = annotation.getNamedParameter(propertyName);
if (value instanceof List<?> && ((List<?>) value).size() > 0
&& ((List<?>) value).get(0) instanceof JavaAnnotation) {
List<Map<String, Object>> recursiveAnnotationList = Lists.newLinkedList();
annotationParameters.put(propertyName, recursiveAnnotationList);
for (JavaAnnotation a : (List<JavaAnnotation>) value) {
Map<String, Object> annotationParameterParameters = new HashMap<>();
extractAnnotationsRecursively(annotationParameterParameters, Lists.newArrayList(a));
recursiveAnnotationList.add(annotationParameterParameters);
}
} else if (value instanceof Enum<?>[]) {
List<String> enumValues = Lists.newLinkedList();
for (Enum<?> e : ((Enum<?>[]) value)) {
enumValues.add(e.name());
}
annotationParameters.put(propertyName, enumValues);
} else if (value instanceof Object[]) {
annotationParameters.put(propertyName, Arrays.asList(value));
annotationParameters.put(propertyName, value);
// annotationParameters.put(propertyName, Lists.newLinkedList(Arrays.asList(value)));
} else if (value instanceof Enum<?>) {
annotationParameters.put(propertyName, ((Enum<?>) value).name());
} else {
// currently QDox only returns the expression stated in the code as value, but not
// resolves it.
annotationParameters.put(propertyName, value);
}
}
Expand Down
Loading

0 comments on commit c681d42

Please sign in to comment.