Package org.mapstruct.ap.model.source

Source Code of org.mapstruct.ap.model.source.SourceReference$PropertyEntry

/**
*  Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
*  and/or other contributors as indicated by the @authors tag. See the
*  copyright.txt file in the distribution for a full listing of all
*  contributors.
*
*  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 org.mapstruct.ap.model.source;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement;
import javax.tools.Diagnostic;

import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Strings;

/**
* This class describes the source side of a property mapping.
* <p>
* It contains the source parameter, and all individual (nested) property entries. So consider the following
* mapping method:
*
* {@code
*
* @author Sjaak Derksen
* @Mapping( source = "in.propA.propB" target = "propC" )
* TypeB mappingMethod ( TypeA in );
* }
*
* Then:
* <ol>
* <li>{@link #parameter} will describe {@code in}</li>
* <li>{@link #propertyEntries[0]} will describe {@code propA}</li>
* <li>{@link #propertyEntries[1]} will describe {@code propB}</li>
* </ol>
*
* After building, {@link #isValid()} will return true when when no problems are detected during building.
*/
public class SourceReference {

    private final Parameter parameter;
    private final List<PropertyEntry> propertyEntries;
    private final boolean isValid;

    /**
     * Builds a {@link SourceReference} from an {@code @Mappping}.
     */
    public static class BuilderFromMapping {

        private Mapping mapping;
        private SourceMethod method;
        private Messager messager;
        private TypeFactory typeFactory;

        public BuilderFromMapping messager(Messager messager) {
            this.messager = messager;
            return this;
        }

        public BuilderFromMapping mapping(Mapping mapping) {
            this.mapping = mapping;
            return this;
        }

        public BuilderFromMapping method(SourceMethod method) {
            this.method = method;
            return this;
        }

        public BuilderFromMapping typeFactory(TypeFactory typeFactory) {
            this.typeFactory = typeFactory;
            return this;
        }

        public SourceReference build() {

            String sourceName = mapping.getSourceName();

            if ( sourceName == null ) {
                return null;
            }

            boolean isValid = true;
            boolean foundEntryMatch;

            String[] sourcePropertyNames = new String[0];
            String[] segments = sourceName.split( "\\." );
            Parameter parameter = null;

            List<PropertyEntry> entries = new ArrayList<PropertyEntry>();

            if ( method.getSourceParameters().size() > 1 ) {

                // parameterName is mandatory for multiple source parameters
                if ( segments.length > 0 ) {
                    String sourceParameterName = segments[0];
                    parameter = method.getSourceParameter( sourceParameterName );
                    if ( parameter == null ) {
                        reportMappingError( "Method has no parameter named \"%s\".", sourceParameterName );
                        isValid = false;
                    }
                }
                if ( segments.length > 1 && parameter != null ) {
                    sourcePropertyNames = Arrays.copyOfRange( segments, 1, segments.length );
                    entries = getSourceEntries( parameter.getType(), sourcePropertyNames );
                    foundEntryMatch = ( entries.size() == sourcePropertyNames.length );
                }
                else {
                    // its only a parameter, no property
                    foundEntryMatch = true;
                }

            }
            else {

                // parameter name is not mandatory for single source parameter
                sourcePropertyNames = segments;
                parameter = method.getSourceParameters().get( 0 );
                entries = getSourceEntries( parameter.getType(), sourcePropertyNames );
                foundEntryMatch = ( entries.size() == sourcePropertyNames.length );

                if ( !foundEntryMatch ) {
                    //Lets see if the expression contains the parameterName, so parameterName.propName1.propName2
                    if ( parameter.getName().equals( segments[0] ) ) {
                        sourcePropertyNames = Arrays.copyOfRange( segments, 1, segments.length );
                        entries = getSourceEntries( parameter.getType(), sourcePropertyNames );
                        foundEntryMatch = ( entries.size() == sourcePropertyNames.length );
                    }
                    else {
                        // segment[0] cannot be attributed to the parameter name.
                        parameter = null;
                    }
                }
            }

            if ( !foundEntryMatch ) {

                if ( parameter != null ) {
                    reportMappingError(
                        "The type of parameter \"%s\" has no property named \"%s\".",
                        parameter.getName(),
                        Strings.join( Arrays.asList( sourcePropertyNames ), "." )
                    );
                }
                else {
                    reportMappingError(
                        "No property named \"%s\" exists in source parameter(s).",
                        mapping.getSourceName()
                    );
                }
                isValid = false;
            }

            return new SourceReference( parameter, entries, isValid );
        }

        private List<PropertyEntry> getSourceEntries(Type type, String[] entryNames) {
            List<PropertyEntry> sourceEntries = new ArrayList<PropertyEntry>();
            Type newType = type;
            for ( String entryName : entryNames ) {
                boolean matchFound = false;
                List<ExecutableElement> getters = newType.getGetters();
                for ( ExecutableElement getter : getters ) {
                    if ( Executables.getPropertyName( getter ).equals( entryName ) ) {
                        newType = typeFactory.getType( getter.getReturnType() );
                        sourceEntries.add( new PropertyEntry( entryName, getter, newType ) );
                        matchFound = true;
                        break;
                    }
                }
                if ( !matchFound ) {
                    break;
                }
            }
            return sourceEntries;
        }

        private void reportMappingError(String message, Object... objects) {
            messager.printMessage(
                Diagnostic.Kind.ERROR,
                String.format( message, objects ),
                method.getExecutable(), mapping.getMirror(),
                mapping.getSourceAnnotationValue()
            );
        }
    }

    /**
     * Builds a {@link SourceReference} from a property.
     */
    public static class BuilderFromProperty {

        private String name;
        private ExecutableElement accessor;
        private Type type;
        private Parameter sourceParameter;

        public BuilderFromProperty name(String name) {
            this.name = name;
            return this;
        }

        public BuilderFromProperty accessor(ExecutableElement accessor) {
            this.accessor = accessor;
            return this;
        }

        public BuilderFromProperty type(Type type) {
            this.type = type;
            return this;
        }

        public BuilderFromProperty sourceParameter(Parameter sourceParameter) {
            this.sourceParameter = sourceParameter;
            return this;
        }

        public SourceReference build() {
            List<PropertyEntry> sourcePropertyEntries = Arrays.asList( new PropertyEntry( name, accessor, type ) );
            return new SourceReference( sourceParameter, sourcePropertyEntries, true );
        }
    }

    private SourceReference(Parameter sourceParameter, List<PropertyEntry> sourcePropertyEntries, boolean isValid) {
        this.parameter = sourceParameter;
        this.propertyEntries = sourcePropertyEntries;
        this.isValid = isValid;
    }

    public Parameter getParameter() {
        return parameter;
    }

    public List<PropertyEntry> getPropertyEntries() {
        return propertyEntries;
    }

    public boolean isValid() {
        return isValid;
    }

    public List<String> getElementNames() {
        List<String> sourceName = new ArrayList<String>();
        sourceName.add( parameter.getName() );
        for ( PropertyEntry propertyEntry : propertyEntries ) {
            sourceName.add( propertyEntry.getName() );
        }
        return sourceName;
    }

    /**
     * A PropertyEntry contains information on the name, accessor and return type of a property.
     */
    public static class PropertyEntry {

        private final String name;
        private final ExecutableElement accessor;
        private final Type type;

        public PropertyEntry(String name, ExecutableElement accessor, Type type) {
            this.name = name;
            this.accessor = accessor;
            this.type = type;
        }

        public String getName() {
            return name;
        }

        public ExecutableElement getAccessor() {
            return accessor;
        }

        public Type getType() {
            return type;
        }

    }
}
TOP

Related Classes of org.mapstruct.ap.model.source.SourceReference$PropertyEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.