/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2011, Open Source Geospatial Foundation (OSGeo) * (C) 2003-2005, Open Geospatial Consortium Inc. * * All Rights Reserved. http://www.opengis.org/legal/ */ package org.opengis.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import org.opengis.metadata.identification.CharacterSet; import org.junit.*; import static org.junit.Assert.*; /** * Tests every {@link CodeList}. * * @author Martin desruisseaux (IRD) * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/opengis/src/test/java/org/opengis/util/CodeListTest.java $ */ public final class CodeListTest { /** * The logger to use. */ private static final Logger LOGGER = Logger.getLogger("org.opengis"); /** * For avoiding to pollute the output stream if {@code ArrayList.capacity()} * method invocation failed. */ private static boolean capacityFailed = false; /** * Tests the {@link CharacterSet} code list. At the difference of other code lists, * its {@link CodeList#matches} method is overriden. */ @Test public void testCharacterSet() { final CodeList code = CharacterSet.UTF_8; assertEquals ("UTF_8", code.name()); assertEquals ("utf8", code.identifier()); assertTrue (code.matches("UTF8")); assertTrue (code.matches("UTF_8")); assertTrue (code.matches("UTF-8")); assertFalse (code.matches("UTF 8")); assertSame (code, CharacterSet.valueOf("UTF_8")); assertSame (code, CharacterSet.valueOf("UTF-8")); assertSame (code, CharacterSet.valueOf("UTF8")); assertSame (code, CharacterSet.valueOf("utf8")); assertNotSame(code, CharacterSet.valueOf("UTF_7")); } /** * Tests the instantiation of every code lists. */ @Test public void testAll() { int count = 0; final Class<CodeList> base = CodeList.class; final ClassScanner scanner = new ClassScanner(); while (scanner.hasNext()) { final Class<?> candidate = scanner.next(); if (!base.equals(candidate) && base.isAssignableFrom(candidate)) { // SimpleEnumeratioType is a special case to avoid for now. final String name = candidate.getName(); if (name.equals("org.opengis.util.SimpleEnumerationType")) { continue; } if (name.equals("org.opengis.filter.sort.SortOrder")) { continue; } assertValid(candidate.asSubclass(CodeList.class)); count++; } } LOGGER.fine("Found " + count + " code lists."); if (count == 0) { LOGGER.warning("No CodeList found."); } } /** * Ensures that the name declared in the code list match the field names. */ private static void assertValid(final Class<? extends CodeList> classe) { Method method; int modifiers; String fullName; /* * Gets the values() method, which should public and static. * Then gets every CodeList instances returned by values(). */ final String className = classe.getName(); fullName = className + ".values()"; try { method = classe.getMethod("values", (Class[]) null); } catch (NoSuchMethodException e) { fail(fullName + " method is missing."); return; } assertNotNull(method); modifiers = method.getModifiers(); assertTrue(fullName + " is not public.", Modifier.isPublic(modifiers)); assertTrue(fullName + " is not static.", Modifier.isStatic(modifiers)); final CodeList[] values; try { values = (CodeList[]) method.invoke(null, (Object[]) null); } catch (IllegalAccessException e) { fail(fullName + " is not accessible."); return; } catch (InvocationTargetException e) { fail("Call to " + fullName + " failed.\n" + e.getTargetException()); return; } assertNotNull(fullName + " returned null.", values); /* * Gets the family() method, to be used when we will test every * code list instances. */ fullName = className + ".family()"; try { method = classe.getMethod("family", (Class[]) null); } catch (NoSuchMethodException e) { fail(fullName + " method is missing."); return; } assertNotNull(method); modifiers = method.getModifiers(); assertTrue (fullName + " is not public.", Modifier.isPublic(modifiers)); assertFalse(fullName + " is static.", Modifier.isStatic(modifiers)); /* * Tests every CodeList instances returned by values(). * Every field should be public, static and final. */ for (final CodeList value : values) { final String name = value.name(); fullName = className + '.' + name; assertTrue(fullName + ": unexpected type.", classe.isInstance(value)); final Field field; try { field = classe.getField(name); } catch (NoSuchFieldException e) { final Class<? extends CodeList> valueClass = value.getClass(); if (!classe.equals(valueClass) && classe.isAssignableFrom(valueClass)) { // Do not fails if valueClass is a subclass of classe. continue; } fail(fullName + " field not found."); continue; } assertNotNull(field); modifiers = field.getModifiers(); assertEquals(fullName + ": unexpected name mismatch.", name, field.getName()); assertTrue (fullName + " is not public.", Modifier.isPublic(modifiers)); assertTrue (fullName + " is not static.", Modifier.isStatic(modifiers)); assertTrue (fullName + " is not final.", Modifier.isFinal (modifiers)); Object constant; try { constant = field.get(null); } catch (IllegalAccessException e) { fail(fullName + " is not accessible."); continue; } assertSame(fullName + " is not the expected instance.", value, constant); final CodeList[] family; try { family = (CodeList[]) method.invoke(constant, (Object[]) null); } catch (IllegalAccessException e) { fail(className + ".family() is not accessible."); return; } catch (InvocationTargetException e) { fail("Call to " + className + ".family() failed.\n" + e.getTargetException()); return; } assertTrue(className + ".family() mismatch.", Arrays.equals(values, family)); } /* * Gets the private VALUES field only if CodeList is the direct parent. */ if (classe.getSuperclass().equals(CodeList.class)) { fullName = className + ".VALUES"; final Field field; try { field = classe.getDeclaredField("VALUES"); } catch (NoSuchFieldException e) { fail(fullName + " private list is missing."); return; } modifiers = field.getModifiers(); assertTrue (Modifier.isStatic (modifiers)); assertTrue (Modifier.isFinal (modifiers)); assertFalse(Modifier.isPublic (modifiers)); assertFalse(Modifier.isProtected(modifiers)); field.setAccessible(true); final ArrayList<?> asList; try { final Object candidate = field.get(null); assertEquals(fullName + " is not an ArrayList.", ArrayList.class, candidate.getClass()); asList = (ArrayList<?>) candidate; } catch (IllegalAccessException e) { fail(className + ".VALUES is not accessible."); return; } assertEquals(Arrays.asList(values), asList); /* * Verifies if the VALUES ArrayList size was properly sized. We need to access to * private ArrayList.elementData field in order to perform this check. Tested on * Sun's JSE 6.0. It is not mandatory to have the VALUES list properly dimensioned; * it just avoid a little bit of memory reallocation at application startup time. */ if (!capacityFailed) { final int capacity; try { final Field candidate = ArrayList.class.getDeclaredField("elementData"); candidate.setAccessible(true); final Object array = candidate.get(asList); capacity = ((Object[]) array).length; } catch (Exception e) { // Not an error, since this test relies on an implementation-specific method. capacityFailed = true; final LogRecord record = new LogRecord(Level.WARNING, e.toString()); record.setThrown(e); record.setLoggerName(LOGGER.getName()); LOGGER.log(record); return; } assertEquals(fullName + " not properly sized.", asList.size(), capacity); } } /* * Tries to create a new element. */ try { method = classe.getMethod("valueOf", String.class); } catch (NoSuchMethodException e) { return; } final CodeList value; try { value = classe.cast(method.invoke(null, "Dummy")); } catch (IllegalAccessException e) { fail(e.toString()); return; } catch (InvocationTargetException e) { e.printStackTrace(); fail(e.getTargetException().toString()); return; } assertEquals("Dummy", value.name()); } }