##  Licensed to the Apache Software Foundation (ASF) under one
##  or more contributor license agreements.  See the NOTICE file
##  distributed with this work for additional information
##  regarding copyright ownership.  The ASF licenses this file
##  to you 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
##
##    https://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.
##
##  Terminology:
## 
##	Base class - super superclass of embeddable, usually java.lang.Object
##  Super class - superclass of embeddable, ie,  org.apache.cayenne.art.auto._Embeddable
##	Sub class - class of entity, ie, org.apache.cayenne.art.Embeddable
##
##  Keys of objects available in template are defined in 'org.apache.cayenne.gen.Artifact', namely:
## 
##    object - the Embeddable class: See org.apache.cayenne.map.Embeddable
##    stringUtils - class for string "helper" functions: See org.apache.cayenne.gen.StringUtils
##    importUtils - class for import statement management: See org.apache.cayenne.gen.ImportUtils
##    superClassName
##    superPackageName
##    subClassName
##    subPackageName
##    baseClassName
##    basePackageName 
##
##
${importUtils.setPackage($superPackageName)}##
${importUtils.addType("java.io.Serializable")}##
${importUtils.addReservedType("${superPackageName}.${superClassName}")}##
${importUtils.addType("org.apache.cayenne.EmbeddableObject")}##
${importUtils.addType("org.apache.cayenne.Persistent")}##
#foreach( $attr in ${object.Attributes} )
$propertyUtils.addImport($attr)##
#end
${importUtils.generate()}

/** 
 * Embeddable class ${superClassName} was generated by Cayenne.
 * It is probably a good idea to avoid changing this class manually, 
 * since it may be overwritten next time code is regenerated. 
 * If you need to make any customizations, please use subclass. 
 */
## extends ${baseClassName} - always Object, so skip it
public abstract class ${superClassName} implements EmbeddableObject, Serializable {
## Create property names
#if( $createPropertyNames )

#foreach( $attr in ${object.Attributes} )
    public static final String ${stringUtils.capitalizedAsConstant($attr.Name)}_PROPERTY = "${attr.Name}";
#end
#end
#######################
## Create Properties ##
#######################

#foreach( $attr in ${object.Attributes} )
    $propertyUtils.propertyDefinition($attr)
#end

    // special properties injected by Cayenne
    private Persistent owner;
    private String embeddedProperty;
    
    // declared properties
#foreach( $attr in ${object.Attributes} )
    protected $importUtils.formatJavaType(${attr.Type}) $stringUtils.formatVariableName(${attr.Name});
#end

    // lifecycle methods
    protected void propertyWillChange(String property, Object oldValue, Object newValue) {
        if (owner != null && owner.getObjectContext() != null) {
            owner.getObjectContext().propertyChanged(
                    owner,
                    embeddedProperty + "." + property,
                    oldValue,
                    newValue);
        }
    }

    // declared getters and setters
## Create attribute set/get methods
#foreach( $attr in ${object.Attributes} )
    public void set${stringUtils.capitalized($attr.Name)}($importUtils.formatJavaType(${attr.Type}) $stringUtils.formatVariableName(${attr.Name})) {
        propertyWillChange("${attr.Name}", this.$stringUtils.formatVariableName(${attr.Name}), $stringUtils.formatVariableName(${attr.Name}));
        this.$stringUtils.formatVariableName(${attr.Name}) = $stringUtils.formatVariableName(${attr.Name});
    }
#if ( $importUtils.isBoolean(${attr.Type}) )
	public boolean is${stringUtils.capitalized($attr.Name)}() {
        return $stringUtils.formatVariableName(${attr.Name});
    }
#elseif ( $importUtils.isNonBooleanPrimitive(${attr.Type}) )
    public $importUtils.formatJavaType(${attr.Type}) get${stringUtils.capitalized($attr.Name)}() {
        return $stringUtils.formatVariableName(${attr.Name});
    }
#else
    public $importUtils.formatJavaType(${attr.Type}) get${stringUtils.capitalized($attr.Name)}() {
        return $stringUtils.formatVariableName(${attr.Name});
    }
#end

#end
###########################################################
## Create writePropertyDirect/readPropertyDirect methods ##
###########################################################
    @Override
    public Object readPropertyDirectly(String propName) {
        if(propName == null) {
            throw new IllegalArgumentException();
        }

        switch(propName) {
#foreach( $attr in ${object.Attributes} )
#set ( $name = "$stringUtils.formatVariableName(${attr.Name})")
            case "${attr.Name}":
                return this.${name};
#end
            default:
                return null;
        }
    }

    @Override
    public void writePropertyDirectly(String propName, Object val) {
        if(propName == null) {
            throw new IllegalArgumentException();
        }

        switch (propName) {
#foreach( $attr in ${object.Attributes} )
#set ( $name = "$stringUtils.formatVariableName(${attr.Name})")
#set ( $flag = $importUtils.canUsePrimitive($attr) )
#set ( $type = "$importUtils.formatJavaType(${attr.Type}, $flag)")
            case "${attr.Name}":
#if ( $importUtils.isBoolean($type) )
                this.${name} = val == null ? false : ($type)val;
#elseif ($importUtils.isPrimitive($type))
                this.${name} = val == null ? 0 : ($type)val;
#else
                this.${name} = ($type)val;
#end
                break;
#end
            default:
                throw new IllegalArgumentException("Unknown property: " + propName);
        }
    }
}
