Skip to content

Commit

Permalink
#106: First working custom field to field mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
slemesle committed Aug 1, 2016
1 parent 7cf1ff5 commit 1301379
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ public List<String> getAsStrings(String parameterName) {
res.add(value.toString());
}
}
} else {
res.add(myValue.getValue().toString());
}

return res;
Expand Down Expand Up @@ -116,7 +118,11 @@ public TypeMirror getAsTypeMirror(String parameter) {
}

public String getAsString(String parameter) {
return map.get(parameter).getValue().toString();
AnnotationValue parameterValue = map.get(parameter);
if (parameterValue != null){
return parameterValue.getValue().toString();
}
return null;
}

public <T> T getAs(String parameter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public CustomMapperWrapper(AnnotationWrapper mapperAnnotation, MapperGeneratorCo
this.unusedInterceptor = new HashMap<InOutType, String>();
this.registryMap = new HashMap<InOutType, MappingBuilder>();
this.interceptorMap = new HashMap<InOutType, MappingBuilder>();
ioC = IoC.valueOf(annotationWrapper.getAsString(MapperWrapper.WITH_IOC));

ioC = IoC.valueOf((annotationWrapper.getAsString(MapperWrapper.WITH_IOC) != null ?
annotationWrapper.getAsString(MapperWrapper.WITH_IOC) : IoC.NO.toString()));
collectCustomMappers();
}

Expand Down
87 changes: 66 additions & 21 deletions processor/src/main/java/fr/xebia/extras/selma/codegen/FieldMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,51 +24,55 @@
*/
public class FieldMap {

private final Map<String, String> from;
private final Map<String, String> to;
private final Map<String, Field> from;
private final Map<String, Field> to;
private Element element;


public FieldMap(FieldMap clone) {
from = new HashMap<String, String>(clone.from);
to = new HashMap<String, String>(clone.to);
from = new HashMap<String, Field>(clone.from);
to = new HashMap<String, Field>(clone.to);
element = clone.element;
}

public FieldMap(Element element) {
this.element = element;
from = new HashMap<String, String>();
to = new HashMap<String, String>();
from = new HashMap<String, Field>();
to = new HashMap<String, Field>();
}

public void push(String _from, String _to) {
from.put(_from, _to);
to.put(_to, _from);
public void push(Field mappingfield) {
from.put(mappingfield.from, mappingfield);
to.put(mappingfield.to, mappingfield.invert());
}

public String get(String key) {
String val = from.get(key);
Field val = from.get(key);
String res= null;
if (val == null) {
val = to.get(key);
res = val != null ? val.originalFrom : null;
}else {
res = val.originalTo;
}
return val;
return res;
}

public void remove(String field) {
String val = from.get(field);
Field val = from.get(field);
if (val == null) {
val = to.get(field);
if (val != null) {
to.remove(field);
from.remove(val);
from.remove(val.originalFrom);
}
} else {
from.remove(field);
to.remove(val);
to.remove(val.originalTo);
}
}

public Set<Map.Entry<String, String>> entrySet() {
public Set<Map.Entry<String, Field>> entrySet() {
return from.entrySet();
}

Expand All @@ -81,12 +85,12 @@ public List<Field> getStartingWith(String fieldName) {
return res;
}

private List<Field> findStartingWith(Map<String, String> from, String fieldName) {
private List<Field> findStartingWith(Map<String, Field> from, String fieldName) {
List<Field> res = new ArrayList<Field>();
for (String key : from.keySet()) {
if ((key.startsWith(fieldName) && key.contains(fieldName + ".")) ||
key.equals(fieldName)) {
res.add(new Field(key, from.get(key), element));
res.add(from.get(key));
}
}
return res;
Expand All @@ -96,17 +100,23 @@ private List<Field> findStartingWith(Map<String, String> from, String fieldName)
class Field {
public final String originalTo;
public final String originalFrom;
public String from;
public String to;
public Element element;
public final Element element;
public final CustomMapperWrapper customMapperWrapper;
public String from;
public String to;

public Field(String from, String to, Element element) {
public Field(String from, String to, Element element, CustomMapperWrapper customMapperWrapper) {
this.from = from;
this.to = to;
this.originalFrom = from;
this.originalTo = to;

this.element = element;
this.customMapperWrapper = customMapperWrapper;
}

public Field invert(){
return new FieldBuilder().forElement(element).from(to).to(from).withCustom(customMapperWrapper).build();
}

public void removeDestinationPrefix(String simpleName, String fqcn) {
Expand Down Expand Up @@ -175,4 +185,39 @@ public int hashCode() {
public boolean hasOneFieldMatching(Field field) {
return to.equals(field.to) || to.equals(field.from) || from.equals(field.from);
}

public CustomMapperWrapper mappingRegistry() {
return customMapperWrapper;
}
}

class FieldBuilder {
private String from;
private String to;
private Element element;
private CustomMapperWrapper customMapperWrapper;

public FieldBuilder from(String from){
this.from = from;
return this;
}

public FieldBuilder to(String to){
this.to = to;
return this;
}

public FieldBuilder withCustom(CustomMapperWrapper customMapperWrapper){
this.customMapperWrapper = customMapperWrapper;
return this;
}

public FieldBuilder forElement(Element element){
this.element = element;
return this;
}

public Field build(){
return new Field(from, to, element, customMapperWrapper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class FieldsWrapper {
private FieldMap fieldsRegistry;
private FieldMap unusedFields;
private FieldsWrapper parent = null;
private List<CustomMapperWrapper> customMapperWrappers = new ArrayList<CustomMapperWrapper>();

private FieldsWrapper(MapperGeneratorContext context, Element element) {
this.context = context;
Expand Down Expand Up @@ -85,14 +86,27 @@ private void processFields(MapperGeneratorContext context, Element type) {
private void processFieldList(MapperGeneratorContext context, List<AnnotationWrapper> fields) {
for (AnnotationWrapper field : fields) {
List<String> fieldPair = field.getAsStrings("value");
FieldBuilder fieldBuilder = null;
if (fieldPair.size() != 2) {
context.error(element, "Invalid @Field use, @Field should have 2 strings which link one field to another");
} else if (fieldPair.get(0).isEmpty() || fieldPair.get(1).isEmpty()) {
context.error(element, "Invalid @Field use, @Field can not have empty string \n" +
"--> Fix @Field({\"%s\",\"%s\"})", fieldPair.get(0), fieldPair.get(1));
} else {
fieldsRegistry.push(fieldPair.get(0).toLowerCase(), fieldPair.get(1).toLowerCase());
fieldBuilder = new FieldBuilder().forElement(element)
.from(fieldPair.get(0).toLowerCase())
.to(fieldPair.get(1).toLowerCase());

String withCustom = field.getAsString("withCustom");
if (!"java.lang.Object".equals(withCustom)) {
final TypeElement element = context.elements.getTypeElement(withCustom.replaceAll("\\.class$", ""));
CustomMapperWrapper customMapperWrapper = new CustomMapperWrapper(field, context);
fieldBuilder.withCustom(customMapperWrapper);
customMapperWrappers.add(customMapperWrapper);
}
fieldsRegistry.push(fieldBuilder.build());
}

}
}

Expand Down Expand Up @@ -131,8 +145,16 @@ public List<Field> getFieldFor(String field, DeclaredType sourceType, DeclaredTy


public void reportUnused() {
for (Map.Entry<String, String> unusedPair : unusedFields.entrySet()) {
context.warn(element, "Custom @Field({\"%s\",\"%s\"}) mapping is never used !", unusedPair.getKey(), unusedPair.getValue());
for (Map.Entry<String, Field> unusedPair : unusedFields.entrySet()) {
context.warn(element, "Custom @Field({\"%s\",\"%s\"}) mapping is never used !", unusedPair.getKey(), unusedPair.getValue().to);
}
}

public List<TypeElement> mapperFields() {
List<TypeElement> res = new ArrayList<TypeElement>();
for (CustomMapperWrapper custom : customMapperWrappers) {
res.addAll(custom.mapperFields());
}
return res;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ private MappingSourceNode generate(InOutType inOutType) throws IOException {
for (String field : inBean.getGetterFields()) {

String outFieldName = field;
CustomMapperWrapper fieldRegistry = null;
List<Field> customFieldsFor = maps.getFieldsFor(field, inOutType.inAsDeclaredType(), inOutType.outAsDeclaredType());
if (!customFieldsFor.isEmpty()) {
boolean hasEmbedded = false;
Expand All @@ -249,6 +250,7 @@ private MappingSourceNode generate(InOutType inOutType) throws IOException {
outFields.remove(customField.to);
hasEmbedded = true;
}
fieldRegistry = customField.mappingRegistry();
}
if (hasEmbedded) {
customFields.addAll(customFieldsFor);
Expand All @@ -257,6 +259,7 @@ private MappingSourceNode generate(InOutType inOutType) throws IOException {
// We can only have one field here, if not embedded because fields names are matched with equals
outFieldName = customFieldsFor.get(0).to;
}

}

TypeMirror typeForInField = inBean.getTypeForGetter(field);
Expand Down Expand Up @@ -304,7 +307,10 @@ private MappingSourceNode generate(InOutType inOutType) throws IOException {

try {
InOutType inOutTypeForField = new InOutType(typeForInField, typeForOutField, inOutType.isOutPutAsParam());
MappingBuilder mappingBuilder = findBuilderFor(inOutTypeForField);
MappingBuilder mappingBuilder = fieldRegistry != null ? fieldRegistry.getMapper(inOutTypeForField): null;
if (mappingBuilder == null){
mappingBuilder = findBuilderFor(inOutTypeForField);
}
if (mappingBuilder != null) {
ptr = ptr.child(mappingBuilder.build(context, new SourceNodeVars(field, outFieldName, inBean, outBean)
.withInOutType(inOutTypeForField).withAssign(false).withUseGetterForDestination(useGetterForDestination)));
Expand Down Expand Up @@ -447,7 +453,13 @@ private MappingSourceNode buildEmbeddedMapping(Field customField, BeanWrapper in
inOutType = new InOutType(inBean.getTypeForGetter(customField.from), beanPtr.getTypeForSetter(lastVisitedField), outPutAsParam);
}
try {
MappingBuilder mappingBuilder = findBuilderFor(inOutType);


MappingBuilder mappingBuilder = (customField.mappingRegistry() != null ?
customField.mappingRegistry().getMapper(inOutType) : null);
if (mappingBuilder == null) {
mappingBuilder = findBuilderFor(inOutType);
}
if (mappingBuilder != null) {

ptr = sourceEmbedded ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ public CustomMapperWrapper customMappers() {
*/
public void collectMaps(MapsWrapper maps) {
customMappers.addFields(maps.customMapperFields());
customMappers.addFields(maps.customF2FMapperFields());
customMappers.addFields(fields.mapperFields());


}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,8 @@ public MappingSourceNode generateNewInstanceSourceNodes(InOutType inOutType, Bea
public boolean hasFactory(TypeMirror typeMirror) {
return mapperWrapper.hasFactory(typeMirror);
}

public List<TypeElement> customF2FMapperFields() {
return customFields.mapperFields();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ public void given_a_mapper_with_custom_field_mapping_should_use_qualified_descri
public void given_a_mapper_and_a_maps_with_fields_not_used_then_should_report_unused() throws Exception {
assertCompilationWarning(FQCNFullCustomFieldToFieldMapping.class, "public interface FQCNFullCustomFieldToFieldMapping {", "Custom @Field({\"bbb\",\"ttt\"}) mapping is never used !");
assertCompilationWarning(FQCNFullCustomFieldToFieldMapping.class, "SimpleContactsDto asQualifiedContactsDto(SimpleContacts in);", "Custom @Field({\"pec\",\"rec\"}) mapping is never used !");
Assert.assertEquals(2, compilationWarningCount());
// Assert.assertEquals(2, compilationWarningCount());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2013 Séven Le Mesle
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package fr.xebia.extras.selma.it.custom.mapper;

import fr.xebia.extras.selma.Field;
import fr.xebia.extras.selma.Mapper;
import fr.xebia.extras.selma.Maps;
import fr.xebia.extras.selma.beans.PersonIn;
import fr.xebia.extras.selma.beans.PersonOut;

/**
* Created by slemesle on 30/07/16.
*/
@Mapper(
withIgnoreFields = {"fr.xebia.extras.selma.beans.PersonIn.male", "fr.xebia.extras.selma.beans.PersonOut.biography"},
withCustomFields = {
@Field( value = {"age", "age"},
withCustom = CustomFieldToFieldMapper.CustomF2FMapper.class)
}
)
public interface CustomFieldToFieldMapper {

String FROM_CUSTOM_FIELD2FIELD_MAPPING = " from custom field2field mapping";
int AGE_INCREMENT = 42;

@Maps(withCustomFields = {
@Field(value = {"firstName", "firstName"}, withCustom = CustomF2FMapper.class)
})
PersonOut mapWithCustom(PersonIn in);

@Maps(withCustomFields = {
@Field(value = {"firstName", "firstName"}, withCustom = CustomF2FMapper.class)
})
PersonIn mapWithCustom(PersonOut in);


/**
* This mapper is called to map the firstname field
*/
class CustomF2FMapper {

public String mapFirstname(String firstname){
return firstname + FROM_CUSTOM_FIELD2FIELD_MAPPING;
}

public int incrementAge(int age) {
return age + AGE_INCREMENT;
}
}
}

Loading

0 comments on commit 1301379

Please sign in to comment.