001 // Copyright 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.EventConstants;
018 import org.apache.tapestry5.Link;
019 import org.apache.tapestry5.ValueEncoder;
020 import org.apache.tapestry5.annotations.ActivationRequestParameter;
021 import org.apache.tapestry5.internal.services.ComponentClassCache;
022 import org.apache.tapestry5.ioc.util.IdAllocator;
023 import org.apache.tapestry5.model.MutableComponentModel;
024 import org.apache.tapestry5.plastic.FieldHandle;
025 import org.apache.tapestry5.plastic.PlasticClass;
026 import org.apache.tapestry5.plastic.PlasticField;
027 import org.apache.tapestry5.runtime.Component;
028 import org.apache.tapestry5.runtime.ComponentEvent;
029 import org.apache.tapestry5.services.ComponentEventHandler;
030 import org.apache.tapestry5.services.Request;
031 import org.apache.tapestry5.services.URLEncoder;
032 import org.apache.tapestry5.services.ValueEncoderSource;
033 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
034 import org.apache.tapestry5.services.transform.TransformationSupport;
035
036 /**
037 * Hooks the activate event handler on the component (presumably, a page) to
038 * extract query parameters, and hooks the link decoration events to extract values
039 * and add them to the {@link Link}.
040 *
041 * @see ActivationRequestParameter
042 * @since 5.2.0
043 */
044 @SuppressWarnings("all")
045 public class ActivationRequestParameterWorker implements ComponentClassTransformWorker2
046 {
047 private final Request request;
048
049 private final ComponentClassCache classCache;
050
051 private final ValueEncoderSource valueEncoderSource;
052
053 private final URLEncoder urlEncoder;
054
055 public ActivationRequestParameterWorker(Request request, ComponentClassCache classCache,
056 ValueEncoderSource valueEncoderSource, URLEncoder urlEncoder)
057 {
058 this.request = request;
059 this.classCache = classCache;
060 this.valueEncoderSource = valueEncoderSource;
061 this.urlEncoder = urlEncoder;
062 }
063
064 public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
065 {
066 for (PlasticField field : plasticClass.getFieldsWithAnnotation(ActivationRequestParameter.class))
067 {
068 mapFieldToQueryParameter(field, support);
069 }
070 }
071
072 private void mapFieldToQueryParameter(PlasticField field, TransformationSupport support)
073 {
074 ActivationRequestParameter annotation = field.getAnnotation(ActivationRequestParameter.class);
075
076 String parameterName = getParameterName(field, annotation);
077
078 // Assumption: the field type is not one that's loaded by the component class loader, so it's safe
079 // to convert to a hard type during class transformation.
080
081 Class fieldType = classCache.forName(field.getTypeName());
082
083 ValueEncoder encoder = valueEncoderSource.getValueEncoder(fieldType);
084
085 FieldHandle handle = field.getHandle();
086
087 String fieldName = String.format("%s.%s", field.getPlasticClass().getClassName(), field.getName());
088
089 setValueFromInitializeEventHandler(support, fieldName, handle, parameterName, encoder, urlEncoder);
090
091 decorateLinks(support, fieldName, handle, parameterName, encoder, urlEncoder);
092
093 preallocateName(support, parameterName);
094 }
095
096
097 private static void preallocateName(TransformationSupport support, final String parameterName)
098 {
099 ComponentEventHandler handler = new ComponentEventHandler()
100 {
101 public void handleEvent(Component instance, ComponentEvent event)
102 {
103 IdAllocator idAllocator = event.getEventContext().get(IdAllocator.class, 0);
104
105 idAllocator.allocateId(parameterName);
106 }
107 };
108
109 support.addEventHandler(EventConstants.PREALLOCATE_FORM_CONTROL_NAMES, 1,
110 "ActivationRequestParameterWorker preallocate form control name '" + parameterName + "' event handler",
111 handler);
112 }
113
114 @SuppressWarnings("all")
115 private void setValueFromInitializeEventHandler(TransformationSupport support, String fieldName, final FieldHandle handle,
116 final String parameterName, final ValueEncoder encoder, final URLEncoder urlEncoder)
117 {
118 ComponentEventHandler handler = new ComponentEventHandler()
119 {
120 public void handleEvent(Component instance, ComponentEvent event)
121 {
122 String clientValue = request.getParameter(parameterName);
123
124 if (clientValue == null)
125 return;
126
127 // TAP5-1768: unescape encoded value
128 clientValue = urlEncoder.decode(clientValue);
129
130 Object value = encoder.toValue(clientValue);
131
132 handle.set(instance, value);
133 }
134 };
135
136 support.addEventHandler(EventConstants.ACTIVATE, 0,
137 String.format("Restoring field %s from query parameter '%s'", fieldName, parameterName), handler);
138
139 }
140
141 @SuppressWarnings("all")
142 private static void decorateLinks(TransformationSupport support, String fieldName, final FieldHandle handle,
143 final String parameterName, final ValueEncoder encoder, final URLEncoder urlEncoder)
144 {
145 ComponentEventHandler handler = new ComponentEventHandler()
146 {
147 public void handleEvent(Component instance, ComponentEvent event)
148 {
149 Object value = handle.get(instance);
150
151 if (value == null)
152 {
153 return;
154 }
155
156 Link link = event.getEventContext().get(Link.class, 0);
157
158 String clientValue = encoder.toClient(value);
159
160 // TAP5-1768: escape special characters
161 clientValue = urlEncoder.encode(clientValue);
162
163 link.addParameter(parameterName, clientValue);
164 }
165 };
166
167 support.addEventHandler(EventConstants.DECORATE_COMPONENT_EVENT_LINK, 0,
168 String.format("ActivationRequestParameterWorker decorate component event link event handler for field %s as query parameter '%s'", fieldName, parameterName), handler);
169
170 support.addEventHandler(EventConstants.DECORATE_PAGE_RENDER_LINK, 0, String.format(
171 "ActivationRequestParameterWorker decorate page render link event handler for field %s as query parameter '%s'", fieldName, parameterName), handler);
172 }
173
174 private String getParameterName(PlasticField field, ActivationRequestParameter annotation)
175 {
176 if (annotation.value().equals(""))
177 return field.getName();
178
179 return annotation.value();
180 }
181
182 }