    001    // Copyright 2006, 2007, 2008, 2010, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.internal.transform;
    016
    017    import org.apache.tapestry5.annotations.Environmental;
    018    import org.apache.tapestry5.internal.services.ComponentClassCache;
    019    import org.apache.tapestry5.model.MutableComponentModel;
    020    import org.apache.tapestry5.plastic.*;
    021    import org.apache.tapestry5.services.Environment;
    022    import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
    023    import org.apache.tapestry5.services.transform.TransformationSupport;
    024
    025    /**
    026     * Obtains a value from the {@link Environment} service based on the field type. This is triggered by the presence of
    027     * the {@link Environmental} annotation.
    028     */
    029    @SuppressWarnings("rawtypes")
    030    public class EnvironmentalWorker implements ComponentClassTransformWorker2
    031    {
    032        private final Environment environment;
    033
    034        private final ComponentClassCache classCache;
    035
    036
    037        @SuppressWarnings("unchecked")
    038        private final class EnvironmentalConduit implements FieldConduit
    039        {
    040            private final String componentClassName;
    041
    042            private final String fieldName;
    043
    044            private final Class environmentalType;
    045
    046            private final boolean required;
    047
    048            private EnvironmentalConduit(String componentClassName, String fieldName, final Class environmentalType,
    049                    boolean required)
    050            {
    051                this.componentClassName = componentClassName;
    052                this.fieldName = fieldName;
    053                this.environmentalType = environmentalType;
    054                this.required = required;
    055            }
    056
    057            public Object get(Object instance, InstanceContext context)
    058            {
    059                return required ? environment.peekRequired(environmentalType) : environment.peek(environmentalType);
    060            }
    061
    062            public void set(Object instance, InstanceContext context, Object newValue)
    063            {
    064                throw new RuntimeException(String.format("Field %s.%s is read only.", componentClassName, fieldName));
    065            }
    066        }
    067
    068        public EnvironmentalWorker(Environment environment, ComponentClassCache classCache)
    069        {
    070            this.environment = environment;
    071
    072            this.classCache = classCache;
    073        }
    074
    075        public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
    076        {
    077            for (PlasticField field : plasticClass.getFieldsWithAnnotation(Environmental.class))
    078            {
    079                transform(model.getComponentClassName(), field);
    080            }
    081        }
    082
    083        private void transform(final String componentClassName, PlasticField field)
    084        {
    085            Environmental annotation = field.getAnnotation(Environmental.class);
    086
    087            field.claim(annotation);
    088
    089            final String fieldName = field.getName();
    090
    091            final Class fieldType = classCache.forName(field.getTypeName());
    092
    093            final boolean required = annotation.value();
    094
    095            ComputedValue<FieldConduit<Object>> provider = new ComputedValue<FieldConduit<Object>>()
    096            {
    097                public FieldConduit<Object> get(InstanceContext context)
    098                {
    099                    return new EnvironmentalConduit(componentClassName, fieldName, fieldType, required);
    100                }
    101
    102                public void set(Object instance, InstanceContext context, Object newValue)
    103                {
    104                    throw new RuntimeException(
    105                            String.format("Field %s of component %s is read only.", fieldName, componentClassName));
    106                }
    107            };
    108
    109            field.setComputedConduit(provider);
    110        }
    111
    112    }