Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
Implementations of metadata derived from ISO 19115. This module provides both an implementation
of the metadata interfaces defined in GeoAPI, and a framework for handling those metadata through
Java reflection.
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2007-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.metadata;
import java.util.Map;
import java.util.Set;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Collection;
import java.util.Locale;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.text.Format;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.ParseException;
import javax.swing.tree.TreeNode;
import org.opengis.util.CodeList;
import org.opengis.util.GenericName;
import org.opengis.util.InternationalString;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.content.Band;
import org.opengis.metadata.spatial.Dimension;
import org.opengis.metadata.spatial.GridSpatialRepresentation;
import org.geotoolkit.util.Strings;
import org.geotoolkit.util.ArgumentChecks;
import org.geotoolkit.util.MeasurementRange;
import org.geotoolkit.util.converter.Classes;
import org.geotoolkit.util.converter.Numbers;
import org.geotoolkit.util.collection.CheckedContainer;
import org.geotoolkit.internal.jaxb.NonMarshalledAuthority;
import org.geotoolkit.gui.swing.tree.TreeTableNode;
import org.geotoolkit.gui.swing.tree.NamedTreeNode;
import org.geotoolkit.gui.swing.tree.MutableTreeNode;
import org.geotoolkit.gui.swing.tree.DefaultMutableTreeNode;
import org.geotoolkit.gui.swing.tree.TreeFormat;
import org.geotoolkit.gui.swing.tree.Trees;
import org.geotoolkit.internal.InternalUtilities;
import org.geotoolkit.internal.CodeLists;
import org.geotoolkit.resources.Errors;
import static org.geotoolkit.metadata.AbstractMetadata.LOGGER;
/**
* Builds tree or tree-table representations of metadata. The root of the tree and each children
* are {@link TreeNode} or {@link TreeTableNode} objects. The node labels are formatted according
* the {@link Locale} argument given at construction time.
*
* This class offers two families of methods, listed in two rows below. The {@code Object} type
* stands for any metadata object compliant with the {@link MetadataStandard} specified at
* construction time.
*
*
*
Parsing
Formatting
*
Between {@link String} and metadata:
*
{@link #parseObject(String)} ⇒ {@link Object}
*
{@link #format(Object)} ⇒ {@link String}
*
Between {@link TreeNode} and metadata:
*
{@link #parse(TreeNode)} ⇒ {@link Object}
*
{@link #asTree(Object)} ⇒ {@link TreeNode}
*
*
* The methods performing conversions between {@link String} and metadata objects are part of the
* {@link Format} contract. They work by converting between {@link String} and {@link TreeNode}
* using {@link TreeFormat}, then delegating to the second family of methods defined in this class
* for the conversions between {@link TreeNode} and metadata object. This second family of methods
* is specific to this {@code MetadataTreeFormat} class.
*
* {@note The TreeNode interface is defined in the javax.swing.tree
* package. However it can be used as a data structure independent of Swing.}
*
* {@section Subclassing}
* This class provides some protected methods that subclasses can override in order to control
* the parsing and formatting processes:
*
*
*
Formating
Parsing
*
*
{@link #formatElementName(Class, String)}
*
{@link #formatCodeList(CodeList)}
*
{@link #formatNumber(Number)}
*
*
*
{@link #getTypeForName(String)}
*
*
* @author Martin Desruisseaux (Geomatys)
* @author Mehdi Sidhoum (Geomatys)
* @version 3.20
*
* @since 2.4
* @module
*/
public class MetadataTreeFormat extends Format {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = -3603011614118221049L;
/**
* The character used for formating number when enumerating the elements of a collection.
*/
static final char OPEN_BRACKET = '[', CLOSE_BRACKET = ']';
/**
* Maximum number of characters to use in the heuristic titles.
* This is an arbitrary limit.
*
* @since 3.19
*/
private static final int TITLE_LIMIT = 40;
/**
* The kind of objects to use for inferring a title, in preference order.
* {@code Object.class} must exist and be last.
*
* @since 3.19
*/
private static final Class>[] TITLE_TYPES = {
GenericName.class, InternationalString.class, CharSequence.class, CodeList.class, Object.class
};
/**
* The standard implemented by the metadata to represent as trees.
*/
protected final MetadataStandard standard;
/**
* The locale to use for {@linkplain InternationalString international string} formatting.
* By default, this is the locale for the {@linkplain java.util.Locale.Category#DISPLAY display}
* category.
*/
protected final Locale displayLocale;
/**
* The locale to use for {@linkplain Date date} and {@linkplain Number number} formatting.
* By default, this is the locale for the {@linkplain java.util.Locale.Category#FORMAT format}
* category.
*/
protected final Locale formatLocale;
/**
* The object to use for formatting numbers, dates and trees. Will be created only when first
* needed. We declare the generic {@link Format} type in order to avoid too early class loading.
*/
private transient Format numberFormat, dateFormat, treeFormat;
/**
* Creates a new tree builder using the default locale.
*
* @param standard The expected standard implemented by the metadata.
*/
public MetadataTreeFormat(final MetadataStandard standard) {
this.standard = standard;
this.displayLocale = Locale.getDefault();
this.formatLocale = displayLocale; // JDK7 allows a different value.
}
/**
* Creates a new tree builder.
*
* @param standard The expected standard implemented by the metadata.
* @param locale The locale to use for {@linkplain Date date}, {@linkplain Number number}
* and {@linkplain InternationalString international string} formatting.
*/
public MetadataTreeFormat(final MetadataStandard standard, final Locale locale) {
this.standard = standard;
this.displayLocale = locale;
this.formatLocale = locale;
}
// ---------------------- PARSING -------------------------------------------------------------
/**
* Creates a metadata from the given string representation, or returns {@code null} if an
* error occurred while parsing the tree.
*
* The default implementation delegates to {@link #parseObject(String)}.
*
* @param text The string representation of the tree to parse.
* @param pos The position when to start the parsing.
* @return The parsed tree as a metadata object, or {@code null} if the given tree can not be parsed.
*/
@Override
public Object parseObject(String text, final ParsePosition pos) {
final int base = pos.getIndex();
text = text.substring(base);
try {
final Object metadata = parseObject(text);
pos.setIndex(base + text.length());
return metadata;
} catch (ParseException e) {
pos.setErrorIndex(base + e.getErrorOffset());
return null;
}
}
/**
* Creates a metadata from the given string representation. The default implementation
* {@linkplain TreeFormat#parseObject(String) converts the given string representation
* to tree nodes}, then invokes {@link #parse(TreeNode)}.
*
* Note that in current implementation, there is a very limited amount of names that the
* {@link #getTypeForName(String)} method can recognize. Users may need to override the
* {@code getTypeForName} method in order to parse their metadata.
*
* @param text The string representation of the tree to parse.
* @return The parsed tree as a metadata object.
* @throws ParseException If an error occurred while parsing the tree.
*/
@Override
public Object parseObject(final String text) throws ParseException {
return parse(getFormat(TreeFormat.class).parseObject(text));
}
/**
* Creates a metadata from the values in every nodes of the given tree. The metadata object
* type is inferred by the {@linkplain NamedTreeNode name} of the given root node.
* For example if the current {@linkplain #standard} is {@link MetadataStandard#ISO_19115}
* and the name of the given root node is {@code "Metadata"}, then the default implementation
* will instantiate a new {@link org.geotoolkit.metadata.iso.DefaultMetadata} object.
*
* Note that in current implementation, there is a very limited amount of names that the
* {@link #getTypeForName(String)} method can recognize. Users may need to override the
* {@code getTypeForName} method in order to parse their metadata.
*
* @param node The node from which to fetch the values.
* @return The parsed tree as a metadata object.
* @throws ParseException If the given {@code metadata} argument is null and its type can not
* be inferred, or if a value can not be stored in the metadata object.
*/
public Object parse(final TreeNode node) throws ParseException {
Object name = node;
if (node instanceof NamedTreeNode) {
name = ((NamedTreeNode) node).getName();
}
final Object metadata = newInstance(standard.getImplementation(getTypeForName(name.toString())));
parse(node, metadata);
return metadata;
}
/**
* Fetches values from every nodes of the given tree except the root, and puts them in
* the given metadata object. The value of the root node is ignored.
*
* If the given metadata object already contains property values, then the parsing will be
* merged with the existing values: attributes not defined in the tree will be left unchanged,
* and collections will be augmented with new entries without change in the previously existing
* entries.
*
* @param node The node from which to fetch the values.
* @param destination The metadata where to store the values, or {@code null} for creating a new one.
* @throws ParseException If the given {@code metadata} argument is null and its type can not
* be inferred, or if a value can not be stored in the metadata object.
*/
final void parse(final TreeNode node, final Object destination) throws ParseException {
final Class> type = destination.getClass();
final PropertyAccessor accessor = standard.getAccessorOptional(type);
if (accessor == null) {
throw new ParseException(Errors.format(Errors.Keys.UNKNOWN_TYPE_$1, type), 0);
}
final int duplicated = parse(node, type, destination, null, accessor);
if (duplicated != 0) {
final LogRecord record = Errors.getResources(displayLocale).getLogRecord(
Level.WARNING, Errors.Keys.DUPLICATED_VALUES_COUNT_$1, duplicated);
record.setSourceClassName("MetadataTreeFormat");
record.setSourceMethodName("parse");
record.setLoggerName(LOGGER.getName());
LOGGER.log(record);
}
}
/**
* Fetches values from every nodes of the given tree except the root, and puts them in
* the given metadata object. This method invokes itself recursively.
*
* @param node The node from which to fetch the values.
* @param type The implementation class of the given {@code metadata}.
* @param metadata The metadata where to store the values, or {@code null} if it should
* be created from the given {@code type} when first needed.
* @param addTo If non-null, then metadata objects (including the one provided to this
* method) are added to this collection after they have been parsed.
* @param accessor The object to use for writing in the metadata object.
* @return The number of duplicated elements, for logging purpose.
* @throws ParseException If a value can not be stored in the given metadata object.
*/
private int parse(final TreeNode node, final Class extends T> type, T metadata,
final Collection super T> addTo, final PropertyAccessor accessor) throws ParseException
{
int duplicated = 0;
final Set done = new HashSet();
final int childCount = node.getChildCount();
for (int i=0; i childType = accessor.type(index, TypeValuePolicy.ELEMENT_TYPE);
if (childType == null) {
// The type of the parameter is unknown (actually the message is a bit
// misleading since it doesn't said that only the type is unknown).
throw new ParseException(Errors.format(Errors.Keys.UNKNOWN_PARAMETER_$1, name), 0);
}
childType = standard.getImplementation(childType);
/*
* If the type is an other metadata implementation, invokes this method
* recursively for every childs of the metadata object we just found.
*/
Object value;
final PropertyAccessor ca = standard.getAccessorOptional(childType);
if (ca != null) {
value = accessor.get(index, metadata);
if (value instanceof Collection>) {
@SuppressWarnings("unchecked") // We will rely on CheckedCollection checks.
final Collection