/projects/geotools-9.2/modules/library/referencing/src/main/java/org/geotools/parameter/ParameterWriter.java
Java | 601 lines | 363 code | 21 blank | 217 comment | 88 complexity | d05dceaff4a60fe102aaf9c50ee586b8 MD5 | raw file
- /*
- * GeoTools - The Open Source Java GIS Toolkit
- * http://geotools.org
- *
- * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
- *
- * 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.geotools.parameter;
- import java.io.FilterWriter;
- import java.io.IOException;
- import java.io.Writer;
- import java.lang.reflect.Array;
- import java.text.DateFormat;
- import java.text.NumberFormat;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Date;
- import java.util.LinkedHashMap;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.Set;
- import org.opengis.metadata.Identifier;
- import org.opengis.parameter.GeneralParameterDescriptor;
- import org.opengis.parameter.GeneralParameterValue;
- import org.opengis.parameter.ParameterDescriptor;
- import org.opengis.parameter.ParameterDescriptorGroup;
- import org.opengis.parameter.ParameterValue;
- import org.opengis.parameter.ParameterValueGroup;
- import org.opengis.referencing.IdentifiedObject;
- import org.opengis.referencing.operation.OperationMethod;
- import org.opengis.util.InternationalString;
- import org.opengis.util.GenericName;
- import org.geotools.io.TableWriter;
- import org.geotools.measure.Angle;
- import org.geotools.measure.AngleFormat;
- import org.geotools.resources.Arguments;
- import org.geotools.resources.Classes;
- import org.geotools.resources.XArray;
- import org.geotools.resources.i18n.Vocabulary;
- import org.geotools.resources.i18n.VocabularyKeys;
- /**
- * Format {@linkplain ParameterDescriptorGroup parameter descriptors} or
- * {@linkplain ParameterValueGroup parameter values} in a tabular format.
- * This writer assumes a monospaced font and an encoding capable to provide
- * drawing box characters (e.g. unicode).
- *
- * @since 2.1
- *
- *
- * @source $URL$
- * @version $Id$
- * @author Martin Desruisseaux
- */
- public class ParameterWriter extends FilterWriter {
- /**
- * The locale.
- */
- private Locale locale = Locale.getDefault();
- /**
- * The formatter to use for numbers. Will be created only when first needed.
- */
- private transient NumberFormat numberFormat;
- /**
- * The formatter to use for dates. Will be created only when first needed.
- */
- private transient DateFormat dateFormat;
- /**
- * The formatter to use for angles. Will be created only when first needed.
- */
- private transient AngleFormat angleFormat;
- /**
- * Creates a new formatter writting parameters to the
- * {@linkplain System#out default output stream}.
- */
- public ParameterWriter() {
- this(Arguments.getWriter(System.out));
- }
- /**
- * Creates a new formatter writting parameters to the specified output stream.
- */
- public ParameterWriter(final Writer out) {
- super(out);
- }
- /**
- * Prints the elements of an operation to the
- * {@linkplain System#out default output stream}.
- * This is a convenience method for <code>new
- * ParameterWriter().{@linkplain #format(OperationMethod) format}(operation)</code>.
- */
- public static void print(final OperationMethod operation) {
- final ParameterWriter writer = new ParameterWriter();
- try {
- writer.format(operation);
- } catch (IOException exception) {
- // Should never happen, since we are writting to System.out.
- throw new AssertionError(exception);
- }
- }
- /**
- * Prints the elements of a descriptor group to the
- * {@linkplain System#out default output stream}.
- * This is a convenience method for <code>new
- * ParameterWriter().{@linkplain #format(ParameterDescriptorGroup)
- * format}(descriptor)</code>.
- */
- public static void print(final ParameterDescriptorGroup descriptor) {
- final ParameterWriter writer = new ParameterWriter();
- try {
- writer.format(descriptor);
- } catch (IOException exception) {
- // Should never happen, since we are writting to System.out.
- throw new AssertionError(exception);
- }
- }
- /**
- * Prints the elements of a parameter group to the
- * {@linkplain System#out default output stream}.
- * This is a convenience method for <code>new
- * ParameterWriter().{@linkplain #format(ParameterValueGroup)
- * format}(values)</code>.
- */
- public static void print(final ParameterValueGroup values) {
- final ParameterWriter writer = new ParameterWriter();
- try {
- writer.format(values);
- } catch (IOException exception) {
- // Should never happen, since we are writting to System.out.
- throw new AssertionError(exception);
- }
- }
- /**
- * Prints the elements of an operation to the output stream.
- *
- * @param operation The operation method to format.
- * @throws IOException if an error occured will writing to the stream.
- */
- public void format(final OperationMethod operation) throws IOException {
- synchronized (lock) {
- format(operation.getName().getCode(), operation.getParameters(), null);
- }
- }
- /**
- * Prints the elements of a descriptor group to the output stream.
- *
- * @param descriptor The descriptor group to format.
- * @throws IOException if an error occured will writing to the stream.
- */
- public void format(final ParameterDescriptorGroup descriptor) throws IOException {
- synchronized (lock) {
- format(descriptor.getName().getCode(), descriptor, null);
- }
- }
- /**
- * Prints the elements of a parameter group to the output stream.
- *
- * @param values The parameter group to format.
- * @throws IOException if an error occured will writing to the stream.
- */
- public void format(final ParameterValueGroup values) throws IOException {
- final ParameterDescriptorGroup descriptor = values.getDescriptor();
- synchronized (lock) {
- format(descriptor.getName().getCode(), descriptor, values);
- }
- }
- /**
- * Implementation of public {@code format} methods.
- *
- * @param name The group name, usually {@code descriptor.getCode().getName()}.
- * @param descriptor The parameter descriptor. Should be equals to
- * {@code values.getDescriptor()} if {@code values} is non null.
- * @param values The parameter values, or {@code null} if none.
- * @throws IOException if an error occured will writing to the stream.
- */
- private void format(final String name,
- final ParameterDescriptorGroup group,
- final ParameterValueGroup values)
- throws IOException
- {
- /*
- * Write the operation name (including aliases) before the table.
- */
- final String lineSeparator = System.getProperty("line.separator", "\n");
- out.write(' ');
- out.write(name);
- out.write(lineSeparator);
- Collection<GenericName> alias = group.getAlias();
- if (alias != null) {
- boolean first = true;
- for (final GenericName a : alias) {
- out.write(first ? " alias " : " ");
- out.write(a.toInternationalString().toString(locale));
- out.write(lineSeparator);
- first = false;
- }
- }
- /*
- * Format the table header (i.e. column names).
- */
- final Vocabulary resources = Vocabulary.getResources(locale);
- final TableWriter table = new TableWriter(out, TableWriter.SINGLE_VERTICAL_LINE);
- table.setMultiLinesCells(true);
- table.writeHorizontalSeparator();
- table.write(resources.getString(VocabularyKeys.NAME));
- table.nextColumn();
- table.write(resources.getString(VocabularyKeys.CLASS));
- table.nextColumn();
- table.write("Minimum"); // TODO localize
- table.nextColumn();
- table.write("Maximum"); // TODO localize
- table.nextColumn();
- table.write(resources.getString((values==null) ? VocabularyKeys.DEFAULT_VALUE
- : VocabularyKeys.VALUE));
- table.nextColumn();
- table.write("Units"); // TODO localize
- table.nextLine();
- table.nextLine(TableWriter.DOUBLE_HORIZONTAL_LINE);
- /*
- * Format each element in the parameter group. If values were supplied, we will
- * iterate through the values instead of the descriptor. We do it that way because
- * the descriptor can't know which optional values are included and which one are
- * omitted.
- */
- List<Object> deferredGroups = null;
- final Object[] array1 = new Object[1];
- final Collection<?> elements = (values!=null) ? values.values() : group.descriptors();
- for (final Object element : elements) {
- final GeneralParameterValue generalValue;
- final GeneralParameterDescriptor generalDescriptor;
- if (values != null) {
- generalValue = (GeneralParameterValue) element;
- generalDescriptor = generalValue.getDescriptor();
- } else {
- generalValue = null;
- generalDescriptor = (GeneralParameterDescriptor) element;
- }
- /*
- * If the current element is a group, we will format it later (after
- * all ordinary elements) in order avoid breaking the table layout.
- */
- if (generalDescriptor instanceof ParameterDescriptorGroup) {
- if (deferredGroups == null) {
- deferredGroups = new ArrayList<Object>();
- }
- deferredGroups.add(element);
- continue;
- }
- /*
- * Format the element name, including all alias (if any).
- * Each alias will be formatted on its own line.
- */
- final Identifier identifier = generalDescriptor.getName();
- table.write(identifier.getCode());
- alias = generalDescriptor.getAlias();
- if (alias != null) {
- for (final GenericName a : alias) {
- if (!identifier.equals(a)) {
- table.write(lineSeparator);
- table.write(a.tip().toInternationalString().toString(locale));
- }
- }
- }
- table.nextColumn();
- /*
- * Format the current element as an ordinary descriptor. If we are iterating
- * over the descriptors rather than values, then the "value" column will be
- * filled with the default value specified in descriptors.
- */
- if (generalDescriptor instanceof ParameterDescriptor) {
- final ParameterDescriptor descriptor = (ParameterDescriptor) generalDescriptor;
- table.write(Classes.getShortName(descriptor.getValueClass()));
- table.nextColumn();
- table.setAlignment(TableWriter.ALIGN_RIGHT);
- Object value = descriptor.getMinimumValue();
- if (value != null) {
- table.write(formatValue(value));
- }
- table.nextColumn();
- value = descriptor.getMaximumValue();
- if (value != null) {
- table.write(formatValue(value));
- }
- table.nextColumn();
- if (generalValue != null) {
- value = ((ParameterValue) generalValue).getValue();
- } else {
- value = descriptor.getDefaultValue();
- }
- /*
- * Wraps the value in an array. Because it may be an array of primitive
- * type, we can't cast to Object[]. Then, each array's element will be
- * formatted on its own line.
- */
- final Object array;
- if (value!=null && value.getClass().isArray()) {
- array = value;
- } else {
- array = array1;
- array1[0] = value;
- }
- final int length = Array.getLength(array);
- for (int i=0; i<length; i++) {
- value = Array.get(array, i);
- if (value != null) {
- if (i != 0) {
- table.write(lineSeparator);
- }
- table.write(formatValue(value));
- }
- }
- table.nextColumn();
- table.setAlignment(TableWriter.ALIGN_LEFT);
- value = descriptor.getUnit();
- if (value != null) {
- table.write(value.toString());
- }
- }
- table.writeHorizontalSeparator();
- }
- table.flush();
- /*
- * Now format all groups deferred to the end of this table.
- * Most of the time, there is no such group.
- */
- if (deferredGroups != null) {
- for (final Object element : deferredGroups) {
- final ParameterValueGroup value;
- final ParameterDescriptorGroup descriptor;
- if (element instanceof ParameterValueGroup) {
- value = (ParameterValueGroup) element;
- descriptor = value.getDescriptor();
- } else {
- value = null;
- descriptor = (ParameterDescriptorGroup) element;
- }
- out.write(lineSeparator);
- format(name + '/' + descriptor.getName().getCode(), descriptor, value);
- }
- }
- }
- /**
- * Formats a summary of a collection of {@linkplain IdentifiedObject identified objects}.
- * The summary contains the identifier name and alias aligned in a table.
- *
- * @param parameters The collection of parameters to format.
- * @param scopes The set of scopes to include in the table, of {@code null} for all
- * of them. A restricted a set will produce a table with less columns.
- * @throws IOException if an error occured will writing to the stream.
- */
- public void summary(final Collection<? extends IdentifiedObject> parameters,
- final Set<String> scopes) throws IOException
- {
- /*
- * Prepares the list of alias before any write to the output stream.
- * We need to prepare the list first, because not all identified objects
- * may have generic names with the same scopes in the same order.
- *
- * titles - The column number for each column title.
- * names - The names (including alias) for each line.
- */
- final Map<Object,Integer> titles = new LinkedHashMap<Object,Integer>();
- final List<String[]> names = new ArrayList<String[]>();
- final Locale locale = this.locale; // Protect from changes.
- String[] descriptions = null;
- titles.put(null, 0); // Special value for the identifier column.
- for (final IdentifiedObject element : parameters) {
- final Collection<GenericName> aliases = element.getAlias();
- String[] elementNames = new String[titles.size()];
- elementNames[0] = element.getName().getCode();
- if (aliases != null) {
- /*
- * The primary name has been fetch (before this block) for one element, and we
- * determined that some alias may be available in addition. Add local alias
- * (i.e. names without their scope) to the 'elementNames' row.
- */
- int count = 0;
- for (final GenericName alias : aliases) {
- final GenericName scope = alias.scope().name();
- final GenericName name = alias.tip();
- final Object title;
- if (scope != null) {
- if (scopes!=null && !scopes.contains(scope.toString())) {
- /*
- * The user requested only a subset of alias (the 'scopes' argument),
- * and the current alias is not a member of this subset. Continue the
- * search to other alias.
- */
- continue;
- }
- title = scope.toInternationalString().toString(locale);
- } else {
- title = count++;
- }
- /*
- * The alias scope is used as the column's title. If the alias has no scope,
- * then a sequencial number is used instead. Now check if the column already
- * exists. If it exists, fetch its position. If it doesn't exist, inserts the
- * new column at the end of existing columns.
- */
- Integer position = titles.get(title);
- if (position == null) {
- position = titles.size();
- titles.put(title, position);
- }
- /*
- * Now stores the alias local name at the position we just determined above.
- * Note that more than one value may exist for the same column. For example
- * both "WGS 84" and "4326" may appear as EPSG alias (as EPSG name and EPSG
- * identifier respectively), depending how the parameters given by the user
- * were constructed.
- */
- final int index = position.intValue();
- if (index >= elementNames.length) {
- elementNames = XArray.resize(elementNames, index+1);
- }
- final String oldName = elementNames[index];
- final String newName = name.toInternationalString().toString(locale);
- if (oldName==null || oldName.length()>newName.length()) {
- /*
- * Keep the shortest string, since it is often a code used
- * for identification (e.g. EPSG code). It also help to fit
- * the table in the window's width.
- */
- elementNames[index] = newName;
- }
- }
- }
- /*
- * Before to add the name and alias to the list, fetch the remarks (if any).
- * They are stored in a separated list and will appear as the very last column.
- */
- final InternationalString remarks = element.getRemarks();
- if (remarks != null) {
- if (descriptions == null) {
- descriptions = new String[parameters.size()];
- }
- descriptions[names.size()] = remarks.toString(locale);
- }
- names.add(elementNames);
- }
- /*
- * Trim the columns that duplicates the identifier column (#0). This is
- * usually the case of the OGC column (usually #1), since we already use
- * OGC name as the main identifier in most cases.
- */
- final boolean[] hide = new boolean[titles.size()];
- trim: for (int column=hide.length; --column>=1;) {
- for (final String[] alias : names) {
- if (alias.length > column) {
- final String name = alias[column];
- if (name!=null && !name.equals(alias[0])) {
- // No need to looks at the next lines.
- // Move to previous column.
- continue trim;
- }
- }
- }
- // A column duplicating the identifier column has been found.
- hide[column] = true;
- }
- /*
- * Writes the table. The header will contains one column for each alias's
- * scope (or authority) declared in 'titles', in the same order. It will
- * also contains a "Description" column if there is some.
- */
- int column = 0;
- synchronized (lock) {
- final TableWriter table = new TableWriter(out, TableWriter.SINGLE_VERTICAL_LINE);
- table.setMultiLinesCells(true);
- table.writeHorizontalSeparator();
- /*
- * Writes all column headers.
- */
- for (final Object element : titles.keySet()) {
- if (hide[column++]) {
- continue;
- }
- final String title;
- if (element == null) {
- title = "Identifier"; // TODO: localize
- } else if (element instanceof String) {
- title = (String) element;
- } else {
- title = "Alias " + element; // TODO: localize
- }
- table.write(title);
- table.nextColumn();
- }
- if (descriptions != null) {
- table.write("Description"); // TODO: localize
- }
- table.writeHorizontalSeparator();
- /*
- * Writes all row.
- */
- int counter = 0;
- for (final String[] aliases : names) {
- for (column=0; column<hide.length; column++) {
- if (hide[column]) {
- continue;
- }
- if (column < aliases.length) {
- final String alias = aliases[column];
- if (alias != null) {
- table.write(alias);
- }
- }
- table.nextColumn();
- }
- if (descriptions != null) {
- final String remarks = descriptions[counter++];
- if (remarks != null) {
- table.write(remarks);
- }
- }
- table.nextLine();
- }
- table.writeHorizontalSeparator();
- table.flush();
- }
- }
- /**
- * Returns the current locale. Newly constructed {@code ParameterWriter}
- * use the {@linkplain Locale#getDefault system default}.
- */
- public Locale getLocale() {
- return locale;
- }
- /**
- * Set the locale to use for table formatting.
- */
- public void setLocale(final Locale locale) {
- synchronized (lock) {
- this.locale = locale;
- numberFormat = null;
- dateFormat = null;
- angleFormat = null;
- }
- }
- /**
- * Format the specified value as a string. This method is automatically invoked
- * by {@code format(...)} methods. The default implementation format
- * {@link Number}, {@link Date} and {@link Angle} object according the
- * {@linkplain #getLocale current locale}. This method can been overridden if
- * more objects need to be formatted in a special way.
- *
- * @param value the value to format.
- * @return The value formatted as a string.
- */
- protected String formatValue(final Object value) {
- if (value instanceof Number) {
- if (numberFormat == null) {
- numberFormat = NumberFormat.getNumberInstance(locale);
- }
- return numberFormat.format(value);
- }
- if (value instanceof Date) {
- if (dateFormat == null) {
- dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
- }
- return dateFormat.format(value);
- }
- if (value instanceof Angle) {
- if (angleFormat == null) {
- angleFormat = AngleFormat.getInstance(locale);
- }
- return angleFormat.format(value);
- }
- return String.valueOf(value);
- }
- }