001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.common.util;
017
018import static org.hamcrest.core.Is.is;
019import static org.hamcrest.core.IsNull.notNullValue;
020import static org.junit.Assert.assertThat;
021import java.lang.reflect.Method;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Map;
025import org.junit.Before;
026import org.junit.Test;
027import org.modeshape.common.CommonI18n;
028import org.modeshape.common.annotation.Category;
029import org.modeshape.common.annotation.Description;
030import org.modeshape.common.annotation.Label;
031import org.modeshape.common.collection.Problem;
032import org.modeshape.common.collection.Problem.Status;
033import org.modeshape.common.i18n.I18n;
034import org.modeshape.common.util.Reflection.Property;
035
036/**
037 * @author Randall Hauch
038 */
039public class ReflectionTest {
040
041    private String string;
042    private List<String> stringList;
043    private Reflection stringReflection;
044    private Reflection stringListReflection;
045
046    @Before
047    public void setUp() {
048        string = "This is string #";
049        stringList = new ArrayList<String>();
050        for (int i = 0; i != 10; ++i) {
051            stringList.add(string + (i + 1));
052        }
053        stringReflection = new Reflection(String.class);
054        stringListReflection = new Reflection(List.class);
055    }
056
057    @Test
058    public void shouldGetClassNameForClass() {
059        assertThat(Reflection.getClassName(String.class), is(String.class.getName()));
060        assertThat(Reflection.getClassName(ArrayList.class), is(ArrayList.class.getName()));
061        assertThat(Reflection.getClassName(StringUtil.class), is(StringUtil.class.getName()));
062    }
063
064    @Test
065    public void shouldGetClassNameOfInterface() {
066        assertThat(Reflection.getClassName(CharSequence.class), is(CharSequence.class.getName()));
067        assertThat(Reflection.getClassName(List.class), is(List.class.getName()));
068    }
069
070    @Test
071    public void shouldGetClassNameWithPrimitive() {
072        assertThat(Reflection.getClassName(Integer.TYPE), is("int"));
073        assertThat(Reflection.getClassName(Boolean.TYPE), is("boolean"));
074        assertThat(Reflection.getClassName(Long.TYPE), is("long"));
075        assertThat(Reflection.getClassName(Short.TYPE), is("short"));
076        assertThat(Reflection.getClassName(Float.TYPE), is("float"));
077        assertThat(Reflection.getClassName(Double.TYPE), is("double"));
078        assertThat(Reflection.getClassName(Character.TYPE), is("char"));
079        assertThat(Reflection.getClassName(Byte.TYPE), is("byte"));
080        assertThat(Reflection.getClassName(Void.TYPE), is("void"));
081    }
082
083    @Test
084    public void shouldGetClassNameWith1DPrimitiveArray() {
085        assertThat(Reflection.getClassName(new int[0].getClass()), is("int[]"));
086        assertThat(Reflection.getClassName(new boolean[0].getClass()), is("boolean[]"));
087        assertThat(Reflection.getClassName(new long[0].getClass()), is("long[]"));
088        assertThat(Reflection.getClassName(new short[0].getClass()), is("short[]"));
089        assertThat(Reflection.getClassName(new float[0].getClass()), is("float[]"));
090        assertThat(Reflection.getClassName(new double[0].getClass()), is("double[]"));
091        assertThat(Reflection.getClassName(new char[0].getClass()), is("char[]"));
092        assertThat(Reflection.getClassName(new byte[0].getClass()), is("byte[]"));
093    }
094
095    @Test
096    public void shouldGetClassNameWith2DPrimitiveArray() {
097        assertThat(Reflection.getClassName(new int[0][0].getClass()), is("int[][]"));
098        assertThat(Reflection.getClassName(new boolean[0][0].getClass()), is("boolean[][]"));
099        assertThat(Reflection.getClassName(new long[0][0].getClass()), is("long[][]"));
100        assertThat(Reflection.getClassName(new short[0][0].getClass()), is("short[][]"));
101        assertThat(Reflection.getClassName(new float[0][0].getClass()), is("float[][]"));
102        assertThat(Reflection.getClassName(new double[0][0].getClass()), is("double[][]"));
103        assertThat(Reflection.getClassName(new char[0][0].getClass()), is("char[][]"));
104        assertThat(Reflection.getClassName(new byte[0][0].getClass()), is("byte[][]"));
105    }
106
107    @Test
108    public void shouldGetClassNameWith3DPrimitiveArray() {
109        assertThat(Reflection.getClassName(new int[0][0][0].getClass()), is("int[][][]"));
110        assertThat(Reflection.getClassName(new boolean[0][0][0].getClass()), is("boolean[][][]"));
111        assertThat(Reflection.getClassName(new long[0][0][0].getClass()), is("long[][][]"));
112        assertThat(Reflection.getClassName(new short[0][0][0].getClass()), is("short[][][]"));
113        assertThat(Reflection.getClassName(new float[0][0][0].getClass()), is("float[][][]"));
114        assertThat(Reflection.getClassName(new double[0][0][0].getClass()), is("double[][][]"));
115        assertThat(Reflection.getClassName(new char[0][0][0].getClass()), is("char[][][]"));
116        assertThat(Reflection.getClassName(new byte[0][0][0].getClass()), is("byte[][][]"));
117    }
118
119    @Test
120    public void shouldGetClassNameWith8DPrimitiveArray() {
121        assertThat(Reflection.getClassName(new int[0][0][0][0][0][0][0][0].getClass()), is("int[][][][][][][][]"));
122        assertThat(Reflection.getClassName(new boolean[0][0][0][0][0][0][0][0].getClass()), is("boolean[][][][][][][][]"));
123        assertThat(Reflection.getClassName(new long[0][0][0][0][0][0][0][0].getClass()), is("long[][][][][][][][]"));
124        assertThat(Reflection.getClassName(new short[0][0][0][0][0][0][0][0].getClass()), is("short[][][][][][][][]"));
125        assertThat(Reflection.getClassName(new float[0][0][0][0][0][0][0][0].getClass()), is("float[][][][][][][][]"));
126        assertThat(Reflection.getClassName(new double[0][0][0][0][0][0][0][0].getClass()), is("double[][][][][][][][]"));
127        assertThat(Reflection.getClassName(new char[0][0][0][0][0][0][0][0].getClass()), is("char[][][][][][][][]"));
128        assertThat(Reflection.getClassName(new byte[0][0][0][0][0][0][0][0].getClass()), is("byte[][][][][][][][]"));
129    }
130
131    @Test
132    public void shouldHaveTargetClass() {
133        assertThat(stringReflection.getTargetClass() == String.class, is(true));
134        assertThat(stringListReflection.getTargetClass() == List.class, is(true));
135    }
136
137    @Test
138    public void shouldFindMethodsWithExactMatchingName() {
139        Method[] stringMethods = stringReflection.findMethods("indexOf", true);
140        assertThat(stringMethods.length, is(4));
141        for (Method method : stringMethods) {
142            assertThat(method.getName(), is("indexOf"));
143        }
144        stringMethods = stringReflection.findMethods("length", true);
145        assertThat(stringMethods.length, is(1));
146        for (Method method : stringMethods) {
147            assertThat(method.getName(), is("length"));
148        }
149    }
150
151    @Test
152    public void shouldFindMethodsWithNameMatchingRegularExpression() {
153        Method[] stringMethods = stringReflection.findMethods("indexO.", true);
154        assertThat(stringMethods.length, is(4));
155        for (Method method : stringMethods) {
156            assertThat(method.getName(), is("indexOf"));
157        }
158        stringMethods = stringReflection.findMethods(".+gth", true);
159        assertThat(stringMethods.length, is(1));
160        for (Method method : stringMethods) {
161            assertThat(method.getName(), is("length"));
162        }
163    }
164
165    @Test
166    public void shouldNotFindMethodsWhenThereAreNoMethodsWithThatName() {
167        assertThat(stringReflection.findMethods("size", true).length, is(0));
168        assertThat(stringListReflection.findMethods("argleBargle", true).length, is(0));
169    }
170
171    @Test
172    public void shouldGetAllPropertiesOnJavaBean() throws Exception {
173        Status status = Status.INFO;
174        int code = 121;
175        I18n msg = CommonI18n.argumentMayNotBeEmpty;
176        Object[] params = new Object[] {"argName"};
177        String resource = "The source";
178        String location = "The place to be";
179        Throwable throwable = null;
180        Problem problem = new Problem(status, code, msg, params, resource, location, throwable);
181        Reflection reflection = new Reflection(Problem.class);
182        List<Property> props = reflection.getAllPropertiesOn(problem);
183        Map<String, Property> propsByName = reflection.getAllPropertiesByNameOn(problem);
184
185        assertThat(props.size(), is(8));
186        assertThat(propsByName.size(), is(8));
187        assertThat(props.containsAll(propsByName.values()), is(true));
188
189        Property property = propsByName.remove("status");
190        assertThat(property.getName(), is("status"));
191        assertThat(property.getLabel(), is("Status"));
192        assertThat(property.getType().equals(Status.class), is(true));
193        assertThat(property.isReadOnly(), is(true));
194        assertThat(property, is(findProperty(property.getName(), props)));
195        assertValue(reflection, problem, property, status);
196
197        property = propsByName.remove("code");
198        assertThat(property.getName(), is("code"));
199        assertThat(property.getLabel(), is("Code"));
200        assertThat(property.getType().equals(Integer.TYPE), is(true));
201        assertThat(property.isReadOnly(), is(true));
202        assertValue(reflection, problem, property, code);
203
204        property = propsByName.remove("message");
205        assertThat(property.getName(), is("message"));
206        assertThat(property.getLabel(), is("Message"));
207        assertThat(property.getType().equals(I18n.class), is(true));
208        assertThat(property.isReadOnly(), is(true));
209        assertValue(reflection, problem, property, msg);
210
211        property = propsByName.remove("messageString");
212        assertThat(property.getName(), is("messageString"));
213        assertThat(property.getLabel(), is("Message String"));
214        assertThat(property.getType().equals(String.class), is(true));
215        assertThat(property.isReadOnly(), is(true));
216        assertValue(reflection, problem, property, msg.text(params));
217
218        property = propsByName.remove("parameters");
219        assertThat(property.getName(), is("parameters"));
220        assertThat(property.getLabel(), is("Parameters"));
221        assertThat(property.getType().equals(Object[].class), is(true));
222        assertThat(property.isReadOnly(), is(true));
223        assertValue(reflection, problem, property, params);
224
225        property = propsByName.remove("resource");
226        assertThat(property.getName(), is("resource"));
227        assertThat(property.getLabel(), is("Resource"));
228        assertThat(property.getType().equals(String.class), is(true));
229        assertThat(property.isReadOnly(), is(true));
230        assertValue(reflection, problem, property, resource);
231
232        property = propsByName.remove("location");
233        assertThat(property.getName(), is("location"));
234        assertThat(property.getLabel(), is("Location"));
235        assertThat(property.getType().equals(String.class), is(true));
236        assertThat(property.isReadOnly(), is(true));
237        assertValue(reflection, problem, property, location);
238
239        property = propsByName.remove("throwable");
240        assertThat(property.getName(), is("throwable"));
241        assertThat(property.getLabel(), is("Throwable"));
242        assertThat(property.getType().equals(Throwable.class), is(true));
243        assertThat(property.isReadOnly(), is(true));
244        assertValue(reflection, problem, property, throwable);
245
246        assertThat(propsByName.isEmpty(), is(true));
247    }
248
249    @Test
250    public void shouldUseAnnotationsOnClassFieldsForProperties() throws Exception {
251        SomeStructure structure = new SomeStructure();
252        structure.setCount(33);
253        structure.setIdentifier("This is the identifier value");
254        Reflection reflection = new Reflection(SomeStructure.class);
255        List<Property> props = reflection.getAllPropertiesOn(structure);
256        Map<String, Property> propsByName = reflection.getAllPropertiesByNameOn(structure);
257
258        assertThat(props.size(), is(3));
259        assertThat(propsByName.size(), is(3));
260
261        Property property = propsByName.remove("identifier");
262        assertThat(property.getName(), is("identifier"));
263        assertThat(property.getLabel(), is(CommonI18n.noMoreContent.text()));
264        assertThat(property.getDescription(), is(CommonI18n.nullActivityMonitorTaskName.text()));
265        assertThat(property.getCategory(), is(CommonI18n.noMoreContent.text()));
266        assertThat(property.getType().equals(String.class), is(true));
267        assertThat(property.isReadOnly(), is(false));
268        assertThat(property, is(findProperty(property.getName(), props)));
269        assertValue(reflection, structure, property, structure.getIdentifier());
270
271        property = propsByName.remove("count");
272        assertThat(property.getName(), is("count"));
273        assertThat(property.getLabel(), is("Count"));
274        assertThat(property.getDescription(), is("This is the count"));
275        assertThat(property.getCategory(), is(""));
276        assertThat(property.getType().equals(Integer.TYPE), is(true));
277        assertThat(property.isReadOnly(), is(false));
278        assertValue(reflection, structure, property, structure.getCount());
279
280        property = propsByName.remove("onFire");
281        assertThat(property.getName(), is("onFire"));
282        assertThat(property.getLabel(), is("On Fire"));
283        assertThat(property.getDescription(), is(""));
284        assertThat(property.getCategory(), is(""));
285        assertThat(property.getType().equals(Boolean.TYPE), is(true));
286        assertThat(property.isReadOnly(), is(true));
287        assertValue(reflection, structure, property, structure.isOnFire());
288    }
289
290    protected void assertValue( Reflection reflection,
291                                Object target,
292                                Property property,
293                                Object expectedValue ) throws Exception {
294        Object actual = reflection.getProperty(target, property);
295        assertThat(actual, is(expectedValue));
296        assertThat(reflection.getPropertyAsString(target, property), is(notNullValue()));
297    }
298
299    protected Property findProperty( String propertyName,
300                                     List<Property> properties ) {
301        for (Property prop : properties) {
302            if (prop.getName().equals(propertyName)) return prop;
303        }
304        return null;
305    }
306
307    protected static class SomeStructure {
308        @Category( i18n = CommonI18n.class, value = "noMoreContent" )
309        @Label( i18n = CommonI18n.class, value = "noMoreContent" )
310        @Description( i18n = CommonI18n.class, value = "nullActivityMonitorTaskName" )
311        private String identifier;
312        @Description( value = "This is the count" )
313        private int count;
314        private boolean onFire;
315
316        public int getCount() {
317            return count;
318        }
319
320        public String getIdentifier() {
321            return identifier;
322        }
323
324        public boolean isOnFire() {
325            return onFire;
326        }
327
328        public void setCount( int count ) {
329            this.count = count;
330        }
331
332        public void setIdentifier( String identifier ) {
333            this.identifier = identifier;
334        }
335
336        // public void setOnFire( boolean onFire ) {
337        // this.onFire = onFire;
338        // }
339    }
340}