001    // Copyright 2004, 2005 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.tapestry.enhance;
016    
017    import static org.easymock.EasyMock.expect;
018    import static org.easymock.EasyMock.expectLastCall;
019    
020    import java.lang.reflect.Modifier;
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.Iterator;
024    import java.util.List;
025    
026    import org.apache.hivemind.ApplicationRuntimeException;
027    import org.apache.hivemind.ErrorLog;
028    import org.apache.hivemind.Location;
029    import org.apache.hivemind.service.BodyBuilder;
030    import org.apache.hivemind.service.MethodSignature;
031    import org.apache.tapestry.BaseComponent;
032    import org.apache.tapestry.BaseComponentTestCase;
033    import org.apache.tapestry.IBinding;
034    import org.apache.tapestry.IComponent;
035    import org.apache.tapestry.binding.BindingSource;
036    import org.apache.tapestry.event.PageDetachListener;
037    import org.apache.tapestry.spec.IComponentSpecification;
038    import org.apache.tapestry.spec.IPropertySpecification;
039    import org.apache.tapestry.spec.PropertySpecification;
040    import org.testng.annotations.Test;
041    
042    /**
043     * Tests for {@link org.apache.tapestry.enhance.SpecifiedPropertyWorker}.
044     * 
045     * @author Howard M. Lewis Ship
046     * @since 4.0
047     */
048    @Test
049    public class TestSpecifiedPropertyWorker extends BaseComponentTestCase
050    {
051        private List buildPropertySpecs(String name, String type, boolean persistent)
052        {
053            return buildPropertySpecs(name, type, persistent, null, null);
054        }
055    
056        private List buildPropertySpecs(String name, String type, boolean persistent,
057                Location location, String initialValue)
058        {
059            PropertySpecification ps = new PropertySpecification();
060            ps.setName(name);
061            ps.setType(type);
062            ps.setPersistence(persistent ? "session" : null);
063            ps.setInitialValue(initialValue);
064            ps.setLocation(location);
065    
066            return Collections.singletonList(ps);
067        }
068    
069        private IComponentSpecification buildComponentSpecification(List propertySpecs)
070        {
071            List names = new ArrayList();
072            Iterator i = propertySpecs.iterator();
073            while (i.hasNext())
074            {
075                IPropertySpecification ps = (IPropertySpecification) i.next();
076    
077                names.add(ps.getName());
078            }
079            
080            IComponentSpecification result = newSpec();
081    
082            expect(result.getPropertySpecificationNames()).andReturn(names);
083    
084            i = propertySpecs.iterator();
085            while (i.hasNext())
086            {
087                IPropertySpecification ps = (IPropertySpecification) i.next();
088    
089                expect(result.getPropertySpecification(ps.getName())).andReturn(ps);
090            }
091    
092            return result;
093        }
094    
095        private IComponentSpecification buildComponentSpecification(String name, String type,
096                boolean persistent)
097        {
098            return buildComponentSpecification(buildPropertySpecs(name, type, persistent));
099        }
100    
101        public enum TestEnum {
102            First,
103            Second
104        }
105        
106        public void test_Can_Proxy_Enum() 
107        {
108            Object obj = TestEnum.First;
109            
110            assert !EnhanceUtils.canProxyPropertyType(obj.getClass());
111            assert EnhanceUtils.canProxyPropertyType(List.class);
112        }
113        
114        public void testAddNormal() throws Exception
115        {
116            Location l = newLocation();
117    
118            IComponentSpecification spec = buildComponentSpecification(buildPropertySpecs(
119                    "fred",
120                    "boolean",
121                    false,
122                    l,
123                    null));
124    
125            // Training
126            EnhancementOperation op = newMock(EnhancementOperation.class);
127    
128            expect(op.convertTypeName("boolean")).andReturn(boolean.class);
129    
130            op.validateProperty("fred", boolean.class);
131            op.claimProperty("fred");
132            op.addField("_$fred", boolean.class);
133    
134            expect(op.getAccessorMethodName("fred")).andReturn("isFred");
135    
136            op.addMethod(
137                    Modifier.PUBLIC,
138                    new MethodSignature(boolean.class, "isFred", null, null),
139                    "return _$fred;",
140                    l);
141    
142            op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "setFred", new Class[]
143            { boolean.class }, null), "{\n  _$fred = $1;\n}\n", l);
144    
145            op.addField("_$fred$default", boolean.class);
146    
147            op.extendMethodImplementation(
148                    IComponent.class,
149                    EnhanceUtils.FINISH_LOAD_SIGNATURE,
150                    "_$fred$default = _$fred;");
151    
152            op.extendMethodImplementation(
153                    PageDetachListener.class,
154                    EnhanceUtils.PAGE_DETACHED_SIGNATURE,
155                    "_$fred = _$fred$default;");
156    
157            replay();
158    
159            SpecifiedPropertyWorker w = new SpecifiedPropertyWorker();
160    
161            w.performEnhancement(op, spec);
162    
163            verify();
164        }
165    
166        public void testAddWithInitialValue() throws Exception
167        {
168            BindingSource bs = newMock(BindingSource.class);
169            Location l = fabricateLocation(12);
170    
171            IComponentSpecification spec = buildComponentSpecification(buildPropertySpecs(
172                    "fred",
173                    "java.util.List",
174                    false,
175                    l,
176                    "ognl:foo()"));
177    
178            InitialValueBindingCreator expectedCreator = new InitialValueBindingCreator(bs,
179                    EnhanceMessages.initialValueForProperty("fred"), "ognl:foo()", l);
180    
181            // Training
182    
183            EnhancementOperation op = newMock(EnhancementOperation.class);
184    
185            expect(op.convertTypeName("java.util.List")).andReturn(List.class);
186    
187            op.validateProperty("fred", List.class);
188            op.claimProperty("fred");
189            op.addField("_$fred", List.class);
190    
191            expect(op.getAccessorMethodName("fred")).andReturn("getFred");
192    
193            op.addMethod(
194                    Modifier.PUBLIC,
195                    new MethodSignature(List.class, "getFred", null, null),
196                    "return _$fred;",
197                    l);
198    
199            op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "setFred", new Class[]
200            { List.class }, null), "{\n  _$fred = $1;\n}\n", l);
201    
202            expect(op.addInjectedField(
203                    "_$fred$initialValueBindingCreator",
204                    InitialValueBindingCreator.class,
205                    expectedCreator)).andReturn("_$fred$initialValueBindingCreator");
206            
207            op.addField("_$fred$initialValueBinding", IBinding.class);
208            op
209                    .extendMethodImplementation(
210                            IComponent.class,
211                            EnhanceUtils.FINISH_LOAD_SIGNATURE,
212                            "_$fred$initialValueBinding = _$fred$initialValueBindingCreator.createBinding(this);\n");
213    
214            expect(op.getClassReference(List.class)).andReturn("_$class$java$util$List");
215    
216            String code = "_$fred = (java.util.List) _$fred$initialValueBinding.getObject(_$class$java$util$List);\n";
217    
218            op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, code);
219            op.extendMethodImplementation(
220                    PageDetachListener.class,
221                    EnhanceUtils.PAGE_DETACHED_SIGNATURE,
222                    code);
223    
224            replay();
225    
226            SpecifiedPropertyWorker w = new SpecifiedPropertyWorker();
227            w.setBindingSource(bs);
228    
229            w.performEnhancement(op, spec);
230    
231            verify();
232        }
233    
234        public void test_Add_Persistent() throws Exception
235        {
236            IComponentSpecification spec = buildComponentSpecification(
237                    "barney",
238                    "java.lang.String",
239                    true);
240    
241            // Training
242    
243            EnhancementOperation op = newMock(EnhancementOperation.class);
244    
245            expect(op.convertTypeName("java.lang.String")).andReturn(String.class);
246    
247            op.validateProperty("barney", String.class);
248            op.claimProperty("barney");
249            op.addField("_$barney", String.class);
250    
251            expect(op.getAccessorMethodName("barney")).andReturn("getBarney");
252    
253            op.addMethod(
254                    Modifier.PUBLIC,
255                    new MethodSignature(String.class, "getBarney", null, null),
256                    "return _$barney;",
257                    null);
258    
259            BodyBuilder b = new BodyBuilder();
260            b.begin();
261            /*
262            b.addln("if ($1 != null && org.apache.tapestry.record.ObservedProperty.class.isAssignableFrom($1.getClass())) {");
263            b.add(" $1 = (" + ClassFabUtils.getJavaClassName(String.class) + ")((org.apache.tapestry.record.ObservedProperty)$1)");
264            b.addln(".getCGProperty();");
265            b.addln("}");
266            */
267            b.addln("org.apache.tapestry.Tapestry#fireObservedChange(this, \"barney\", ($w) $1);");
268            b.addln("_$barney = $1;");
269            b.end();
270    
271            op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "setBarney", new Class[]
272            { String.class }, null), b.toString(), null);
273    
274            op.addField("_$barney$default", String.class);
275    
276            op.extendMethodImplementation(
277                    IComponent.class,
278                    EnhanceUtils.FINISH_LOAD_SIGNATURE,
279                    "_$barney$default = _$barney;");
280    
281            op.extendMethodImplementation(
282                    PageDetachListener.class,
283                    EnhanceUtils.PAGE_DETACHED_SIGNATURE,
284                    "_$barney = _$barney$default;");
285    
286            replay();
287    
288            SpecifiedPropertyWorker w = new SpecifiedPropertyWorker();
289    
290            w.performEnhancement(op, spec);
291    
292            verify();
293        }
294    
295        public void testFailure() throws Exception
296        {
297            Location l = fabricateLocation(207);
298            // Should be "java.lang.Long"
299            List propertySpecs = buildPropertySpecs("wilma", "Long", false, l, null);
300            IComponentSpecification spec = buildComponentSpecification(propertySpecs);
301    
302            EnhancementOperation op = newMock(EnhancementOperation.class);
303    
304            op.convertTypeName("Long");
305            Throwable ex = new ApplicationRuntimeException("Simulated error.");
306            expectLastCall().andThrow(ex);
307            
308    
309            expect(op.getBaseClass()).andReturn(BaseComponent.class);
310    
311            ErrorLog log = newMock(ErrorLog.class);
312    
313            log
314                    .error(
315                            "Error adding property wilma to class org.apache.tapestry.BaseComponent: Simulated error.",
316                            l,
317                            ex);
318    
319            replay();
320    
321            SpecifiedPropertyWorker w = new SpecifiedPropertyWorker();
322            w.setErrorLog(log);
323    
324            w.performEnhancement(op, spec);
325    
326            verify();
327        }
328    
329    }