/** * Returns the filter for simplifying the tree table to be formatted. * This is used only for the tree in text format (not for XML output). * * <p>We omit the "Metadata standard" node because it is hard-coded to the same value in all Apache SIS {@code DataStore} * implementations, and that hard-coded value is verbose. The value will be shown in XML output, which is verbose anyway.</p> */ @Override Predicate<TreeTable.Node> getNodeFilter() { return (node) -> !"metadataStandard".equals(node.getValue(TableColumn.IDENTIFIER)); } }
if (node.getValue(VALUE_AS_TEXT) == null) { final TreeTable.Node child = children.remove(0); final StringBuilder name = new StringBuilder(node.getValue(NAME)); if (!File.separator.contentEquals(name)) { name.append(File.separatorChar); child.setValue(NAME, name.append(child.getValue(NAME))); return child;
/** * If a file path in the given node or any children follow the Maven pattern, remove the * artifact name and version numbers redundancies in order to make the name more compact. * For example this method replaces {@code "org/opengis/geoapi/3.0.0/geoapi-3.0.0.jar"} * by {@code "org/opengis/(…)/geoapi-3.0.0.jar"}. */ private static void omitMavenRedundancy(final TreeTable.Node node) { for (final TreeTable.Node child : node.getChildren()) { omitMavenRedundancy(child); } final CharSequence name = node.getValue(NAME); final int length = name.length(); final int s2 = CharSequences.lastIndexOf(name, File.separatorChar, 0, length); if (s2 >= 0) { final int s1 = CharSequences.lastIndexOf(name, File.separatorChar, 0, s2); if (s1 >= 0) { final int s0 = CharSequences.lastIndexOf(name, File.separatorChar, 0, s1) + 1; final StringBuilder buffer = new StringBuilder(s2 - s0).append(name, s0, s2); buffer.setCharAt(s1 - s0, '-'); if (CharSequences.regionMatches(name, s2+1, buffer)) { buffer.setLength(0); node.setValue(NAME, buffer.append(name, 0, s0).append("(…)").append(name, s2, length)); } } } }
if (name.equals(child.getValue(column))) { return child;
if (name.equals(child.getValue(column))) { return child;
if (node.getValue(VALUE_AS_TEXT) == null) { final TreeTable.Node child = children.remove(0); final StringBuilder name = new StringBuilder(node.getValue(NAME)); if (!File.separator.contentEquals(name)) { name.append(File.separatorChar); child.setValue(NAME, name.append(child.getValue(NAME))); return child;
final Object value = node.getValue(column); if (value != null) { String text;
final Object value = node.getValue(column); if (value != null) { String text;
child.setValue(TableColumn.VALUE, "A new title"); assertTitleEquals("citation", "A new title", citation); assertSame(citation.getTitle(), child.getValue(TableColumn.VALUE)); child.setValue(TableColumn.VALUE, "Third alternate title"); assertEquals(3, citation.getAlternateTitles().size()); assertEquals("Third alternate title", child.getValue(TableColumn.VALUE).toString());
/** * Finds the node for the given file, or creates a new node if none exists. * This method performs the same work than the above variant, but working on * {@code File} instances rather than {@code Path}. * * @param from the root node from which to start the search. * @param column the column containing the file name. * @param path the file for which to find or create a node. * @return the node for the given file, either as an existing node or a new node. */ public static TreeTable.Node nodeForPath(TreeTable.Node from, final TableColumn<? super String> column, final File path) { final File parent = path.getParentFile(); if (parent != null) { from = nodeForPath(from, column, parent); } String name = path.getName(); if (name.isEmpty() && parent == null) { name = File.separator; // Root directory in Unix path syntax. } for (final TreeTable.Node child : from.getChildren()) { if (name.equals(child.getValue(column))) { return child; } } from = from.newChild(); from.setValue(column, name); return from; }
/** * Finds the node for the given file, or creates a new node if none exists. * This method performs the same work than the above variant, but working on * {@code File} instances rather than {@code Path}. * * @param from the root node from which to start the search. * @param column the column containing the file name. * @param path the file for which to find or create a node. * @return the node for the given file, either as an existing node or a new node. */ public static TreeTable.Node nodeForPath(TreeTable.Node from, final TableColumn<? super String> column, final File path) { final File parent = path.getParentFile(); if (parent != null) { from = nodeForPath(from, column, parent); } String name = path.getName(); if (name.isEmpty() && parent == null) { name = File.separator; // Root directory in Unix path syntax. } for (final TreeTable.Node child : from.getChildren()) { if (name.equals(child.getValue(column))) { return child; } } from = from.newChild(); from.setValue(column, name); return from; }
/** * Tests a metadata than can be simplified by displaying a child property value directly as the parent value. */ @Test @DependsOnMethod("testReadOnlyWithoutCollections") public void testSimplifiable() { final DefaultCitation citation = metadataSimplifiable(); /* * DefaultCitation * └─Date * ├─Date………………… 2012-01-01 * └─Date type…… Creation * * We need to perform the tests on the "Date" node, not on the "DefaultCitation" node. */ final TreeTable.Node node = TestUtilities.getSingleton(create(citation, ValueExistencePolicy.COMPACT)); assertEquals("value", 1325376000000L, ((Date) node.getValue(TableColumn.VALUE)).getTime()); final TreeNodeChildren children = (TreeNodeChildren) node.getChildren(); final String[] expected = { // The "Date" node should be omitted because merged with the parent "Date" node. "DateType[CREATION]" }; assertEquals("titleProperty", 0, children.titleProperty); assertFalse ("isEmpty()", children.isEmpty()); assertEquals("size()", expected.length, children.size()); assertAllNextEqual(expected, children.iterator()); }
/** * Adds the given node to this list. This method fetches the object from {@link TableColumn#VALUE} * and assigns it to the property identified by {@link TableColumn#IDENTIFIER}. All other columns * are ignored. * * <p>If the identified property is a collection, then this method adds the value to that collection. * Otherwise the new value will be set only if the previous value is null, * {@linkplain org.apache.sis.xml.NilObject nil} or empty.</p> * * <p>This method does not iterate explicitly through the children list, because adding a metadata * object implicitly adds all its children.</p> * * @param node the node from which to get the values. * @return {@code true} if the metadata changed as a result of this method call. * @throws NullPointerException if the given node is null. * @throws IllegalArgumentException if this list does not have a property for the node identifier. * @throws IllegalStateException if a value already exists and no more value can be added for the node identifier. * @throws UnmodifiableMetadataException if the property for the node identifier is read-only. * @throws ClassCastException if the node value can not be converted to the expected type. * @throws BackingStoreException if the metadata implementation threw a checked exception. */ @Override public boolean add(final TreeTable.Node node) throws IllegalStateException { final String identifier = node.getValue(TableColumn.IDENTIFIER); if (identifier == null) { throw new IllegalArgumentException(Errors.format( Errors.Keys.MissingValueInColumn_1, TableColumn.IDENTIFIER.getHeader())); } return add(accessor.indexOf(identifier, true), node.getValue(TableColumn.VALUE)); }
/** * Adds the given node to this list. This method fetches the object from {@link TableColumn#VALUE} * and assigns it to the property identified by {@link TableColumn#IDENTIFIER}. All other columns * are ignored. * * <p>If the identified property is a collection, then this method adds the value to that collection. * Otherwise the new value will be set only if the previous value is null, * {@linkplain org.apache.sis.xml.NilObject nil} or empty.</p> * * <p>This method does not iterate explicitly through the children list, because adding a metadata * object implicitly adds all its children.</p> * * @param node the node from which to get the values. * @return {@code true} if the metadata changed as a result of this method call. * @throws NullPointerException if the given node is null. * @throws IllegalArgumentException if this list does not have a property for the node identifier. * @throws IllegalStateException if a value already exists and no more value can be added for the node identifier. * @throws UnmodifiableMetadataException if the property for the node identifier is read-only. * @throws ClassCastException if the node value can not be converted to the expected type. * @throws BackingStoreException if the metadata implementation threw a checked exception. */ @Override public boolean add(final TreeTable.Node node) throws IllegalStateException { final String identifier = node.getValue(TableColumn.IDENTIFIER); if (identifier == null) { throw new IllegalArgumentException(Errors.format( Errors.Keys.MissingValueInColumn_1, TableColumn.IDENTIFIER.getHeader())); } return add(accessor.indexOf(identifier, true), node.getValue(TableColumn.VALUE)); }
/** * If a file path in the given node or any children follow the Maven pattern, remove the * artifact name and version numbers redundancies in order to make the name more compact. * For example this method replaces {@code "org/opengis/geoapi/3.0.0/geoapi-3.0.0.jar"} * by {@code "org/opengis/(…)/geoapi-3.0.0.jar"}. */ private static void omitMavenRedundancy(final TreeTable.Node node) { for (final TreeTable.Node child : node.getChildren()) { omitMavenRedundancy(child); } final CharSequence name = node.getValue(NAME); final int length = name.length(); final int s2 = CharSequences.lastIndexOf(name, File.separatorChar, 0, length); if (s2 >= 0) { final int s1 = CharSequences.lastIndexOf(name, File.separatorChar, 0, s2); if (s1 >= 0) { final int s0 = CharSequences.lastIndexOf(name, File.separatorChar, 0, s1) + 1; final StringBuilder buffer = new StringBuilder(s2 - s0).append(name, s0, s2); buffer.setCharAt(s1 - s0, '-'); if (CharSequences.regionMatches(name, s2+1, buffer)) { buffer.setLength(0); node.setValue(NAME, buffer.append(name, 0, s0).append("(…)").append(name, s2, length)); } } } }
/** * Tests the {@link TreeTables#replaceCharSequences(TreeTable, Locale)} method. */ @Test public void testReplaceCharSequences() { final TreeTable table = new DefaultTreeTable(NAME, VALUE_AS_NUMBER); final TreeTable.Node root = table .getRoot(); final TreeTable.Node parent = root .newChild(); final TreeTable.Node child1 = parent.newChild(); final TreeTable.Node child2 = root .newChild(); root .setValue(NAME, new StringBuilder("Root")); parent.setValue(NAME, "A parent"); child1.setValue(NAME, new StringBuilder("A child")); child2.setValue(NAME, new SimpleInternationalString("A child")); root .setValue(VALUE_AS_NUMBER, 8); parent.setValue(VALUE_AS_NUMBER, 4); final String asString = table.toString(); assertEquals(3, replaceCharSequences(table, null)); assertInstanceOf("replaceCharSequences:", String.class, root .getValue(NAME)); assertInstanceOf("replaceCharSequences:", String.class, parent.getValue(NAME)); assertInstanceOf("replaceCharSequences:", String.class, child1.getValue(NAME)); assertInstanceOf("replaceCharSequences:", String.class, child2.getValue(NAME)); assertSame("Expected unique instance of String.", child1.getValue(NAME), child2.getValue(NAME)); assertEquals("String representation shall be the same.", asString, table.toString()); } }
/** * The {@code concatenateSingletons(…)} example documented in the {@link TreeTables} class javadoc. * This simple code assumes that the children collection in the given node is a {@link List}. * * @param node the root of the node to simplify. * @return the root of the simplified tree. May be the given {@code node} or a child. */ public static TreeTable.Node concatenateSingletons(final TreeTable.Node node) { final List<TreeTable.Node> children = (List<TreeTable.Node>) node.getChildren(); final int size = children.size(); for (int i=0; i<size; i++) { children.set(i, concatenateSingletons(children.get(i))); } if (size == 1) { final TreeTable.Node child = children.get(0); if (node.getValue(VALUE_AS_TEXT) == null) { children.remove(0); child.setValue(NAME, node.getValue(NAME) + File.separator + child.getValue(NAME)); return child; } } return node; }
/** * Search in given tree node direct children for a node with a table column equals to some specific value. * If we cannot find any matching, we add a new child into input node, initiated with the wanted value. * @param <T> Table column value type. * @param parent Node to search into (only direct children) * @param searchCriteria The table column to search in. * @param searchValue Searched value. * @return A matching node, created if necessary. */ public static <T> TreeTable.Node getOrCreateNode(final TreeTable.Node parent, final TableColumn<T> searchCriteria, final T searchValue) { for (final TreeTable.Node n : parent.getChildren()) { if (searchValue.equals(n.getValue(searchCriteria))) { return n; } } final TreeTable.Node newChild = parent.newChild(); newChild.setValue(searchCriteria, searchValue); return newChild; } }
/** * Implementation of the above {@code assertGetterReturns}, to be invoked recursively. * * @return number of nodes found in the given metadata tree. */ private static int assertColumnContentEquals(final TreeTable.Node node, final TableColumn<?> column, final Object[] expected, int index) { final Object actual = node.getValue(column); Object unlocalized = actual; if (unlocalized instanceof InternationalString) { unlocalized = ((InternationalString) unlocalized).toString(Locale.ROOT); } assertEquals("values[" + index + ']', expected[index++], unlocalized); for (final TreeTable.Node child : node.getChildren()) { index = assertColumnContentEquals(child, column, expected, index); } assertSame("Value shall be stable.", actual, node.getValue(column)); return index; }
/** * Tests {@link DefaultTreeTable.Node#setValue(TableColumn, Object)}. */ @Test public void testNodeValues() { final DefaultTreeTable table = new DefaultTreeTable(NAME, TYPE); final TreeTable.Node node = new DefaultTreeTable.Node(table); assertNull(node.getValue(NAME)); assertNull(node.getValue(TYPE)); node.setValue(NAME, "A number"); node.setValue(TYPE, Number.class); assertEquals("A number", node.getValue(NAME)); assertEquals(Number.class, node.getValue(TYPE)); } }