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 }