Search in sources :

Example 1 with Vocabulary

use of org.apache.sis.util.resources.Vocabulary in project sis by apache.

the class HelpCommand method help.

/**
 * Implementation of {@link #run()}, also shared by {@link CommandRunner#help(String)}.
 *
 * @param  showHeader    {@code true} for printing the "Apache SIS" header.
 * @param  commandNames  the names of the commands to list.
 * @param  validOptions  the options to list.
 * @throws IOException should never happen, because we are writing to a {@code PrintWriter}.
 */
void help(final boolean showHeader, final String[] commandNames, final EnumSet<Option> validOptions) throws IOException {
    final ResourceBundle commands = ResourceBundle.getBundle("org.apache.sis.console.Commands", locale);
    final ResourceBundle options = ResourceBundle.getBundle("org.apache.sis.console.Options", locale);
    final Vocabulary vocabulary = Vocabulary.getResources(locale);
    if (showHeader) {
        out.print("Apache SIS, ");
        out.println(commands.getString("SIS"));
        out.println(commands.getString("Usage"));
        out.println();
        out.print(vocabulary.getString(Vocabulary.Keys.Commands));
        out.println(':');
    }
    final TableAppender table = new TableAppender(out, "  ");
    for (final String command : commandNames) {
        if (showHeader) {
            table.append("  ");
        }
        table.append(command);
        if (!showHeader) {
            table.append(':');
        }
        table.nextColumn();
        table.append(commands.getString(command));
        table.nextLine();
    }
    table.flush();
    out.println();
    out.print(vocabulary.getString(Vocabulary.Keys.Options));
    out.println(':');
    for (final Option option : validOptions) {
        final String name = option.label();
        table.append("  ").append(Option.PREFIX).append(name);
        table.nextColumn();
        table.append(options.getString(name));
        table.nextLine();
    }
    table.flush();
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) TableAppender(org.apache.sis.io.TableAppender) ResourceBundle(java.util.ResourceBundle)

Example 2 with Vocabulary

use of org.apache.sis.util.resources.Vocabulary in project sis by apache.

the class ParameterFormat method formatSummary.

/**
 * Implementation of public {@code format(…)} methods for {@code NAME_SUMMARY} content level.
 *
 * @param  objects  the collection of objects to format.
 * @param  out      the stream or buffer where to write the summary.
 * @throws IOException if an error occurred will writing to the given appendable.
 */
private void formatSummary(final IdentifiedObject[] objects, final Appendable out) throws IOException {
    final Vocabulary resources = Vocabulary.getResources(displayLocale);
    /*
         * Prepares all rows before we write them to the output stream, because not all
         * identified objects may have names with the same scopes in the same order. We
         * also need to iterate over all rows in order to know the number of columns.
         *
         * The first column is reserved for the identifier. We put null as a sentinal key for
         * that column name, to be replaced later by "Identifier" in user locale. We can not
         * put the localized strings in the map right now because they could conflict with
         * the scope of some alias to be processed below.
         */
    boolean hasIdentifiers = false;
    final List<String[]> rows = new ArrayList<>();
    final Map<String, Integer> columnIndices = new LinkedHashMap<>();
    // See above comment for the meaning of "null" here.
    columnIndices.put(null, 0);
    if (preferredCodespaces != null) {
        for (final String codespace : preferredCodespaces) {
            columnIndices.put(codespace, columnIndices.size());
        }
    }
    for (final IdentifiedObject object : objects) {
        // Will growth later if needed.
        String[] row = new String[columnIndices.size()];
        /*
             * Put the first identifier in the first column. If no identifier has a codespace in the list
             * supplied by the user, then we will use the first identifier (any codespace) as a fallback.
             */
        final Set<ReferenceIdentifier> identifiers = object.getIdentifiers();
        if (identifiers != null) {
            // Paranoiac check.
            Identifier identifier = null;
            for (final ReferenceIdentifier candidate : identifiers) {
                if (candidate != null) {
                    // Paranoiac check.
                    if (isPreferredCodespace(candidate.getCodeSpace())) {
                        identifier = candidate;
                        // Format now.
                        break;
                    }
                    if (identifier == null) {
                        // To be used as a fallback if we find nothing better.
                        identifier = candidate;
                    }
                }
            }
            if (identifier != null) {
                row[0] = IdentifiedObjects.toString(identifier);
                hasIdentifiers = true;
            }
        }
        /*
             * If the name's codespace is in the list of codespaces asked by the user, add that name
             * in the current row and clear the 'name' locale variable. Otherwise, keep the 'name'
             * locale variable in case we found no alias to format.
             */
        ReferenceIdentifier name = object.getName();
        if (name != null) {
            // Paranoiac check.
            final String codespace = name.getCodeSpace();
            if (isPreferredCodespace(codespace)) {
                row = putIfAbsent(resources, row, columnIndices, codespace, name.getCode());
                name = null;
            }
        }
        /*
             * Put all aliases having a codespace in the list asked by the user.
             */
        final Collection<GenericName> aliases = object.getAlias();
        if (aliases != null) {
            // Paranoiac check.
            for (final GenericName alias : aliases) {
                if (alias != null) {
                    // Paranoiac check.
                    final String codespace = NameToIdentifier.getCodeSpace(alias, displayLocale);
                    if (isPreferredCodespace(codespace)) {
                        row = putIfAbsent(resources, row, columnIndices, codespace, alias.tip().toInternationalString().toString(displayLocale));
                        name = null;
                    }
                }
            }
        }
        /*
             * If no name and no alias have a codespace in the list of codespaces asked by the user,
             * force the addition of primary name regardless its codespace.
             */
        if (name != null) {
            row = putIfAbsent(resources, row, columnIndices, name.getCodeSpace(), name.getCode());
        }
        rows.add(row);
    }
    /*
         * Writes the table. The header will contain one column for each codespace in the order declared
         * by the user. If the user did not specified any codespace, or if we had to write codespace not
         * on the user list, then those codespaces will be written in the order we found them.
         */
    final boolean hasColors = (colors != null);
    final TableAppender table = new TableAppender(out, columnSeparator);
    table.setMultiLinesCells(true);
    table.appendHorizontalSeparator();
    for (String codespace : columnIndices.keySet()) {
        if (codespace == null) {
            // Skip empty column.
            if (!hasIdentifiers)
                continue;
            codespace = resources.getString(Vocabulary.Keys.Identifier);
        }
        if (hasColors) {
            codespace = X364.BOLD.sequence() + codespace + X364.NORMAL.sequence();
        }
        table.append(codespace);
        nextColumn(table);
    }
    table.appendHorizontalSeparator();
    /*
         * Writes row content.
         */
    final int numColumns = columnIndices.size();
    for (final String[] row : rows) {
        for (int i = hasIdentifiers ? 0 : 1; i < numColumns; i++) {
            if (i < row.length) {
                final String name = row[i];
                if (name != null) {
                    table.append(name);
                }
            }
            nextColumn(table);
        }
        table.nextLine();
    }
    table.appendHorizontalSeparator();
    table.flush();
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) ArrayList(java.util.ArrayList) TableAppender(org.apache.sis.io.TableAppender) LinkedHashMap(java.util.LinkedHashMap) GenericName(org.opengis.util.GenericName) ReferenceIdentifier(org.opengis.referencing.ReferenceIdentifier) NameToIdentifier(org.apache.sis.internal.metadata.NameToIdentifier) Identifier(org.opengis.metadata.Identifier) ReferenceIdentifier(org.opengis.referencing.ReferenceIdentifier) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Example 3 with Vocabulary

use of org.apache.sis.util.resources.Vocabulary in project sis by apache.

the class FeatureFormat method format.

/**
 * Formats the given object to the given stream of buffer.
 * The object may be an instance of any of the following types:
 *
 * <ul>
 *   <li>{@code Feature}</li>
 *   <li>{@code FeatureType}</li>
 * </ul>
 *
 * @throws IOException if an error occurred while writing to the given appendable.
 */
@Override
public void format(final Object object, final Appendable toAppendTo) throws IOException {
    ArgumentChecks.ensureNonNull("object", object);
    ArgumentChecks.ensureNonNull("toAppendTo", toAppendTo);
    /*
         * Separate the Feature (optional) and the FeatureType (mandatory) instances.
         */
    final DefaultFeatureType featureType;
    final AbstractFeature feature;
    if (object instanceof AbstractFeature) {
        feature = (AbstractFeature) object;
        featureType = feature.getType();
    } else if (object instanceof DefaultFeatureType) {
        featureType = (DefaultFeatureType) object;
        feature = null;
    } else {
        throw new IllegalArgumentException(Errors.getResources(displayLocale).getString(Errors.Keys.UnsupportedType_1, object.getClass()));
    }
    /*
         * Computes the columns to show. We start with the set of columns specified by setAllowedColumns(Set),
         * then we check if some of those columns are empty. For example in many cases there is no attribute
         * with characteritic, in which case we will ommit the whole "characteristics" column. We perform such
         * check only for optional information, not for mandatory information like property names.
         */
    final EnumSet<Column> visibleColumns = columns.clone();
    {
        boolean hasDesignation = false;
        boolean hasCharacteristics = false;
        boolean hasDeprecatedTypes = false;
        for (final AbstractIdentifiedType propertyType : featureType.getProperties(true)) {
            if (!hasDesignation) {
                hasDesignation = propertyType.getDesignation() != null;
            }
            if (!hasCharacteristics && propertyType instanceof DefaultAttributeType<?>) {
                hasCharacteristics = !((DefaultAttributeType<?>) propertyType).characteristics().isEmpty();
            }
            if (!hasDeprecatedTypes && propertyType instanceof Deprecable) {
                hasDeprecatedTypes = ((Deprecable) propertyType).isDeprecated();
            }
        }
        if (!hasDesignation)
            visibleColumns.remove(Column.DESIGNATION);
        if (!hasCharacteristics)
            visibleColumns.remove(Column.CHARACTERISTICS);
        if (!hasDeprecatedTypes)
            visibleColumns.remove(Column.REMARKS);
    }
    /*
         * Format the feature type name. In the case of feature type, format also the names of super-type
         * after the UML symbol for inheritance (an arrow with white head). We do not use the " : " ASCII
         * character for avoiding confusion with the ":" separator in namespaces. After the feature (type)
         * name, format the column header: property name, type, cardinality and (default) value.
         */
    toAppendTo.append(toString(featureType.getName()));
    if (feature == null) {
        // UML symbol for inheritance.
        String separator = " ⇾ ";
        for (final FeatureType parent : featureType.getSuperTypes()) {
            toAppendTo.append(separator).append(toString(parent.getName()));
            separator = SEPARATOR;
        }
    }
    toAppendTo.append(getLineSeparator());
    /*
         * Create a table and format the header. Columns will be shown in Column enumeration order.
         */
    final Vocabulary resources = Vocabulary.getResources(displayLocale);
    final TableAppender table = new TableAppender(toAppendTo, columnSeparator);
    table.setMultiLinesCells(true);
    table.nextLine('─');
    boolean isFirstColumn = true;
    for (final Column column : visibleColumns) {
        short key = column.resourceKey;
        if (key == Vocabulary.Keys.Value && feature == null) {
            key = Vocabulary.Keys.DefaultValue;
        }
        if (!isFirstColumn)
            nextColumn(table);
        table.append(resources.getString(key));
        isFirstColumn = false;
    }
    table.nextLine();
    table.nextLine('─');
    /*
         * Done writing the header. Now write all property rows.  For each row, the first part in the loop
         * extracts all information needed without formatting anything yet. If we detect in that part that
         * a row has no value, it will be skipped if and only if that row is optional (minimum occurrence
         * of zero).
         */
    final StringBuffer buffer = new StringBuffer();
    final FieldPosition dummyFP = new FieldPosition(-1);
    final List<String> remarks = new ArrayList<>();
    for (final AbstractIdentifiedType propertyType : featureType.getProperties(true)) {
        Object value = null;
        int cardinality = -1;
        if (feature != null) {
            if (!(propertyType instanceof DefaultAttributeType<?>) && !(propertyType instanceof DefaultAssociationRole) && !DefaultFeatureType.isParameterlessOperation(propertyType)) {
                continue;
            }
            value = feature.getPropertyValue(propertyType.getName().toString());
            if (value == null) {
                if (propertyType instanceof FieldType && ((FieldType) propertyType).getMinimumOccurs() == 0) {
                    // If optional and no value, skip the full row.
                    continue;
                }
                cardinality = 0;
            } else if (value instanceof Collection<?>) {
                cardinality = ((Collection<?>) value).size();
            } else {
                cardinality = 1;
            }
        } else if (propertyType instanceof DefaultAttributeType<?>) {
            value = ((DefaultAttributeType<?>) propertyType).getDefaultValue();
        } else if (propertyType instanceof AbstractOperation) {
            buffer.append(" = ");
            try {
                ((AbstractOperation) propertyType).formatResultFormula(buffer);
            } catch (IOException e) {
                // Should never happen since we write in a StringBuffer.
                throw new UncheckedIOException(e);
            }
            value = CharSequences.trimWhitespaces(buffer).toString();
            buffer.setLength(0);
        }
        // The value to write in the type column.
        final String valueType;
        // AttributeType.getValueClass() if applicable.
        final Class<?> valueClass;
        // Negative values mean no cardinality.
        final int minimumOccurs, maximumOccurs;
        // Result of operation if applicable.
        final AbstractIdentifiedType resultType;
        if (propertyType instanceof AbstractOperation) {
            // May be null
            resultType = ((AbstractOperation) propertyType).getResult();
        } else {
            resultType = propertyType;
        }
        if (resultType instanceof DefaultAttributeType<?>) {
            final DefaultAttributeType<?> pt = (DefaultAttributeType<?>) resultType;
            minimumOccurs = pt.getMinimumOccurs();
            maximumOccurs = pt.getMaximumOccurs();
            valueClass = pt.getValueClass();
            valueType = getFormat(Class.class).format(valueClass, buffer, dummyFP).toString();
            buffer.setLength(0);
        } else if (resultType instanceof DefaultAssociationRole) {
            final DefaultAssociationRole pt = (DefaultAssociationRole) resultType;
            minimumOccurs = pt.getMinimumOccurs();
            maximumOccurs = pt.getMaximumOccurs();
            valueType = toString(DefaultAssociationRole.getValueTypeName(pt));
            valueClass = AbstractFeature.class;
        } else {
            valueType = (resultType != null) ? toString(resultType.getName()) : "";
            valueClass = null;
            minimumOccurs = -1;
            maximumOccurs = -1;
        }
        /*
             * At this point we determined that the row should not be skipped
             * and we got all information to format.
             */
        isFirstColumn = true;
        for (final Column column : visibleColumns) {
            if (!isFirstColumn)
                nextColumn(table);
            isFirstColumn = false;
            switch(column) {
                /*
                     * Human-readable name of the property. May contains any characters (spaces, ideographs, etc).
                     * In many cases, this information is not provided and the whole column is skipped.
                     */
                case DESIGNATION:
                    {
                        final InternationalString d = propertyType.getDesignation();
                        if (d != null)
                            table.append(d.toString(displayLocale));
                        break;
                    }
                /*
                     * Machine-readable name of the property (identifier). This information is mandatory.
                     * This name is usually shorter than the designation and should contain only valid
                     * Unicode identifier characters (e.g. no spaces).
                     */
                case NAME:
                    {
                        table.append(toString(propertyType.getName()));
                        break;
                    }
                /*
                     * The base class or interface for all values in properties of the same type.
                     * This is typically String, Number, Integer, Geometry or URL.
                     */
                case TYPE:
                    {
                        table.append(valueType);
                        break;
                    }
                /*
                     * Minimum and maximum number of occurrences allowed for this property.
                     * If we are formatting a Feature instead than a FeatureType, then the
                     * actual number of values is also formatted. Example: 42 ∈ [0 … ∞]
                     */
                case CARDINALITY:
                    {
                        table.setCellAlignment(TableAppender.ALIGN_RIGHT);
                        if (cardinality >= 0) {
                            table.append(getFormat(Integer.class).format(cardinality, buffer, dummyFP));
                            buffer.setLength(0);
                        }
                        if (maximumOccurs >= 0) {
                            if (cardinality >= 0) {
                                table.append(' ').append((cardinality >= minimumOccurs && cardinality <= maximumOccurs) ? '∈' : '∉').append(' ');
                            }
                            final Format format = getFormat(Integer.class);
                            table.append('[').append(format.format(minimumOccurs, buffer, dummyFP)).append(" … ");
                            buffer.setLength(0);
                            if (maximumOccurs != Integer.MAX_VALUE) {
                                table.append(format.format(maximumOccurs, buffer, dummyFP));
                            } else {
                                table.append('∞');
                            }
                            buffer.setLength(0);
                            table.append(']');
                        }
                        break;
                    }
                /*
                     * If formatting a FeatureType, the default value. If formatting a Feature, the actual value.
                     * A java.text.Format instance dedicated to the value class is used if possible. In addition
                     * to types for which a java.text.Format may be available, we also have to check for other
                     * special cases. If there is more than one value, they are formatted as a coma-separated list.
                     */
                case VALUE:
                    {
                        table.setCellAlignment(TableAppender.ALIGN_LEFT);
                        // Null if valueClass is null.
                        final Format format = getFormat(valueClass);
                        final Iterator<?> it = CollectionsExt.toCollection(value).iterator();
                        String separator = "";
                        int length = 0;
                        while (it.hasNext()) {
                            value = it.next();
                            if (value != null) {
                                if (propertyType instanceof DefaultAssociationRole) {
                                    final String p = DefaultAssociationRole.getTitleProperty((DefaultAssociationRole) propertyType);
                                    if (p != null) {
                                        value = ((AbstractFeature) value).getPropertyValue(p);
                                        if (value == null)
                                            continue;
                                    }
                                } else if (format != null && valueClass.isInstance(value)) {
                                    // Null safe because of getFormat(valueClass) contract.
                                    /*
                                     * Convert numbers, dates, angles, etc. to character sequences before to append them in the table.
                                     * Note that DecimalFormat writes Not-a-Number as "NaN" in some locales and as "�" in other locales
                                     * (U+FFFD - Unicode replacement character). The "�" seems to be used mostly for historical reasons;
                                     * as of 2017 the Unicode Common Locale Data Repository (CLDR) seems to define "NaN" for all locales.
                                     * We could configure DecimalFormatSymbols for using "NaN", but (for now) we rather substitute "�" by
                                     * "NaN" here for avoiding to change the DecimalFormat configuration and for distinguishing the NaNs.
                                     */
                                    final StringBuffer t = format.format(value, buffer, dummyFP);
                                    if (value instanceof Number) {
                                        final float f = ((Number) value).floatValue();
                                        if (Float.isNaN(f)) {
                                            if ("�".contentEquals(t)) {
                                                t.setLength(0);
                                                t.append("NaN");
                                            }
                                            try {
                                                final int n = MathFunctions.toNanOrdinal(f);
                                                if (n > 0)
                                                    t.append(" #").append(n);
                                            } catch (IllegalArgumentException e) {
                                                // May happen if the NaN is a signaling NaN instead than a quiet NaN.
                                                final int bits = Float.floatToRawIntBits(f);
                                                if (bits != illegalNaN) {
                                                    illegalNaN = bits;
                                                    Logging.recoverableException(Logging.getLogger(Modules.FEATURE), FeatureFormat.class, "format", e);
                                                }
                                            }
                                        }
                                    }
                                    value = t;
                                }
                                /*
                                 * All values: the numbers, dates, angles, etc. formatted above, any other character sequences
                                 * (e.g. InternationalString), or other kind of values - some of them handled in a special way.
                                 */
                                length = formatValue(value, table.append(separator), length);
                                buffer.setLength(0);
                                // Value is too long, abandon remaining iterations.
                                if (length < 0)
                                    break;
                                separator = SEPARATOR;
                                length += SEPARATOR.length();
                            }
                        }
                        break;
                    }
                /*
                     * Characteristics are optional information attached to some values. For example if a property
                     * value is a temperature measurement, a characteritic of that value may be the unit of measure.
                     * Characteristics are handled as "attributes of attributes".
                     */
                case CHARACTERISTICS:
                    {
                        if (propertyType instanceof DefaultAttributeType<?>) {
                            int length = 0;
                            String separator = "";
                            format: for (final DefaultAttributeType<?> ct : ((DefaultAttributeType<?>) propertyType).characteristics().values()) {
                                /*
                                 * Format the characteristic name. We will append the value(s) later.
                                 * We keep trace of the text length in order to stop formatting if the
                                 * text become too long.
                                 */
                                final GenericName cn = ct.getName();
                                final String cs = toString(cn);
                                table.append(separator).append(cs);
                                length += separator.length() + cs.length();
                                Collection<?> cv = CollectionsExt.singletonOrEmpty(ct.getDefaultValue());
                                if (feature != null) {
                                    /*
                                     * Usually, the property 'cp' below is null because all features use the same
                                     * characteristic value (for example the same unit of measurement),  which is
                                     * given by the default value 'cv'.  Nevertheless we have to check if current
                                     * feature overrides this characteristic.
                                     */
                                    final Object cp = feature.getProperty(propertyType.getName().toString());
                                    if (cp instanceof AbstractAttribute<?>) {
                                        // Should always be true, but we are paranoiac.
                                        AbstractAttribute<?> ca = ((AbstractAttribute<?>) cp).characteristics().get(cn.toString());
                                        if (ca != null)
                                            cv = ca.getValues();
                                    }
                                }
                                /*
                                 * Now format the value, separated from the name with " = ". Example: unit = m/s
                                 * If the value accepts multi-occurrences, we will format the value between {…}.
                                 * We use {…} because we may have more than one characteristic in the same cell,
                                 * so we need a way to distinguish multi-values from multi-characteristics.
                                 */
                                final boolean multi = ct.getMaximumOccurs() > 1;
                                String sep = multi ? " = {" : " = ";
                                for (Object c : cv) {
                                    length = formatValue(c, table.append(sep), length += sep.length());
                                    // Value is too long, abandon remaining iterations.
                                    if (length < 0)
                                        break format;
                                    sep = SEPARATOR;
                                }
                                separator = SEPARATOR;
                                if (multi && sep == SEPARATOR) {
                                    table.append('}');
                                }
                            }
                        }
                        break;
                    }
                case REMARKS:
                    {
                        if (org.apache.sis.feature.Field.isDeprecated(propertyType)) {
                            table.append(resources.getString(Vocabulary.Keys.Deprecated));
                            final InternationalString r = ((Deprecable) propertyType).getRemarks();
                            if (r != null) {
                                remarks.add(r.toString(displayLocale));
                                appendSuperscript(remarks.size(), table);
                            }
                        }
                        break;
                    }
            }
        }
        table.nextLine();
    }
    table.nextLine('─');
    table.flush();
    /*
         * If there is any remarks, write them below the table.
         */
    final int n = remarks.size();
    for (int i = 0; i < n; i++) {
        appendSuperscript(i + 1, toAppendTo);
        toAppendTo.append(' ').append(remarks.get(i)).append(lineSeparator);
    }
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) TableAppender(org.apache.sis.io.TableAppender) ArrayList(java.util.ArrayList) UncheckedIOException(java.io.UncheckedIOException) InternationalString(org.opengis.util.InternationalString) GenericName(org.opengis.util.GenericName) Format(java.text.Format) TabularFormat(org.apache.sis.io.TabularFormat) Iterator(java.util.Iterator) Deprecable(org.apache.sis.util.Deprecable) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException) FieldPosition(java.text.FieldPosition) InternationalString(org.opengis.util.InternationalString) Collection(java.util.Collection) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Example 4 with Vocabulary

use of org.apache.sis.util.resources.Vocabulary in project sis by apache.

the class LocationFormat method format.

/**
 * Writes a textual representation of the given location in the given stream or buffer.
 *
 * <div class="warning"><b>Upcoming API change — generalization</b><br>
 * in a future SIS version, the type of {@code location} parameter may be generalized
 * to the {@code org.opengis.referencing.gazetteer.Location} interface.
 * This change is pending GeoAPI revision.</div>
 *
 * @param  location    the location to format.
 * @param  toAppendTo  where to format the location.
 * @throws IOException if an error occurred while writing to the given appendable.
 */
@Override
@SuppressWarnings({ "fallthrough", "null" })
public void format(final AbstractLocation location, final Appendable toAppendTo) throws IOException {
    ArgumentChecks.ensureNonNull("location", location);
    final Locale locale = getLocale(Locale.Category.DISPLAY);
    final Vocabulary vocabulary = Vocabulary.getResources(locale);
    final TableAppender table = new TableAppender(toAppendTo, "│ ", columnSeparator, " │");
    table.setMultiLinesCells(true);
    /*
         * Location type.
         */
    table.appendHorizontalSeparator();
    final AbstractLocationType type = location.type();
    if (type != null) {
        append(table, vocabulary, Vocabulary.Keys.LocationType, toString(type.getName(), locale));
    }
    /*
         * Geographic identifier and alternative identifiers, if any.
         */
    append(table, vocabulary, Vocabulary.Keys.GeographicIdentifier, toString(location.getGeographicIdentifier(), locale));
    final Collection<? extends InternationalString> alt = location.getAlternativeGeographicIdentifiers();
    if (alt != null && !alt.isEmpty()) {
        boolean isFirst = true;
        vocabulary.appendLabel(Vocabulary.Keys.AlternativeIdentifiers, table);
        nextColumn(table);
        for (final InternationalString id : alt) {
            if (!isFirst) {
                isFirst = false;
                table.append(lineSeparator);
            }
            table.append(id);
        }
        table.nextLine();
    }
    /*
         * Extents (temporal and geographic). If an envelope exists and the CRS is not geographic,
         * then the envelope bounds will be appended on the same lines than the geographic bounds.
         * But before writing the bounding box and/or the envelope, check if they are redundant.
         * We may also need to change axis order (but not unit) of the envelope in order to match
         * the axis order of the geographic bounding box.
         */
    final Extent extent = new DefaultExtent(null, location.getGeographicExtent(), null, location.getTemporalExtent());
    final Range<Date> time = Extents.getTimeRange(extent);
    if (time != null) {
        append(table, vocabulary, Vocabulary.Keys.StartDate, toString(time.getMinValue()));
        append(table, vocabulary, Vocabulary.Keys.EndDate, toString(time.getMaxValue()));
    }
    GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(extent);
    Envelope envelope = location.getEnvelope();
    DirectPosition position = position(location.getPosition());
    // Position in geographic CRS.
    DirectPosition geopos = null;
    // Envelope Coordinate Reference System.
    CoordinateReferenceSystem crs = null;
    // CRS in conventional (x,y) axis order.
    CoordinateReferenceSystem normCRS = null;
    // If failed to transform envelope.
    Exception warning = null;
    try {
        if (envelope != null) {
            normCRS = normalize(crs = envelope.getCoordinateReferenceSystem());
            if (normCRS != crs) {
                // Should only change order and sign.
                envelope = Envelopes.transform(envelope, normCRS);
            }
        }
        if (position != null) {
            /*
                 * If only one of the envelope or the position objects specify a CRS, assume that the other object
                 * use the same CRS. If both the envelope and the position objects specify a CRS, the envelope CRS
                 * will have precedence and the "representative position" will be projected to that CRS.
                 */
            final CoordinateReferenceSystem posCRS = position.getCoordinateReferenceSystem();
            if (normCRS == null) {
                normCRS = normalize(crs = posCRS);
                if (normCRS != crs) {
                    // Should only change order and sign.
                    envelope = Envelopes.transform(envelope, normCRS);
                }
            }
            if (bbox != null) {
                // Compute geographic position only if there is a geographic bounding box.
                GeographicCRS geogCRS = ReferencingUtilities.toNormalizedGeographicCRS(posCRS);
                if (geogCRS != null) {
                    geopos = transform(position, posCRS, geogCRS);
                }
            }
            position = transform(position, posCRS, normCRS);
        }
    } catch (FactoryException | TransformException e) {
        envelope = null;
        position = null;
        warning = e;
    }
    /*
         * At this point we got the final geographic bounding box and/or envelope to write.
         * Since we will write the projected and geographic coordinates side-by-side in the same cells,
         * we need to format them in advance so we can compute their width for internal right-alignment.
         * We do the alignment ourselves instead than using TableAppender.setCellAlignment(ALIGN_RIGHT)
         * because we do not want (projected geographic) tuple to appear far on the right side if other
         * cells have long texts.
         */
    if (bbox != null || envelope != null) {
        final CoordinateSystem cs = (crs != null) ? crs.getCoordinateSystem() : null;
        String[] geographic = null;
        String[] projected = null;
        String[] unitSymbol = null;
        AngleFormat geogFormat = null;
        NumberFormat projFormat = null;
        UnitFormat unitFormat = null;
        int maxGeogLength = 0;
        int maxProjLength = 0;
        int maxUnitLength = 0;
        boolean showProj = false;
        if (bbox != null || geopos != null) {
            geogFormat = (AngleFormat) getFormat(Angle.class);
            geographic = new String[BOUND_KEY.length];
            Arrays.fill(geographic, "");
        }
        if (envelope != null || position != null) {
            projFormat = (NumberFormat) getFormat(Number.class);
            unitFormat = (UnitFormat) getFormat(Unit.class);
            projected = new String[BOUND_KEY.length];
            unitSymbol = new String[BOUND_KEY.length];
            Arrays.fill(projected, "");
            Arrays.fill(unitSymbol, "");
        }
        for (int i = 0; i < BOUND_KEY.length; i++) {
            RoundingMode rounding = RoundingMode.FLOOR;
            double g = Double.NaN;
            double p = Double.NaN;
            int dimension = 0;
            switch(i) {
                case 0:
                    if (bbox != null)
                        g = bbox.getWestBoundLongitude();
                    if (envelope != null)
                        p = envelope.getMinimum(0);
                    break;
                case 2:
                    if (bbox != null)
                        g = bbox.getEastBoundLongitude();
                    if (envelope != null)
                        p = envelope.getMaximum(0);
                    rounding = RoundingMode.CEILING;
                    break;
                case 3:
                    if (bbox != null)
                        g = bbox.getSouthBoundLatitude();
                    if (envelope != null)
                        p = envelope.getMinimum(1);
                    dimension = 1;
                    break;
                case 5:
                    if (bbox != null)
                        g = bbox.getNorthBoundLatitude();
                    if (envelope != null)
                        p = envelope.getMaximum(1);
                    rounding = RoundingMode.CEILING;
                    dimension = 1;
                    break;
                // Fall through
                case 4:
                    dimension = 1;
                case 1:
                    if (geopos != null)
                        g = geopos.getOrdinate(dimension);
                    if (position != null)
                        p = position.getOrdinate(dimension);
                    rounding = RoundingMode.HALF_EVEN;
                    break;
            }
            if (!Double.isNaN(p)) {
                showProj |= (g != p);
                if (cs != null) {
                    final Unit<?> unit = cs.getAxis(dimension).getUnit();
                    if (unit != null) {
                        final int length = (unitSymbol[i] = unitFormat.format(unit)).length();
                        if (length > maxUnitLength) {
                            maxUnitLength = length;
                        }
                    }
                }
                try {
                    projFormat.setRoundingMode(rounding);
                } catch (UnsupportedOperationException e) {
                // Ignore.
                }
                final int length = (projected[i] = projFormat.format(p)).length();
                if (length > maxProjLength) {
                    maxProjLength = length;
                }
            }
            if (!Double.isNaN(g)) {
                geogFormat.setRoundingMode(rounding);
                final Angle angle = (dimension == 0) ? new Longitude(g) : new Latitude(g);
                final int length = (geographic[i] = geogFormat.format(angle)).length();
                if (length > maxGeogLength) {
                    maxGeogLength = length;
                }
            }
        }
        if (!showProj) {
            // All projected coordinates are identical to geographic ones.
            projected = null;
            unitSymbol = null;
            maxProjLength = 0;
            maxUnitLength = 0;
        } else if (maxProjLength != 0) {
            if (maxUnitLength != 0) {
                maxUnitLength++;
            }
            // Arbitrary space between projected and geographic coordinates.
            maxGeogLength += 4;
        }
        /*
             * At this point all coordinates have been formatted in advance.
             */
        final String separator = (projected != null && geographic != null) ? "    —" : "";
        for (int i = 0; i < BOUND_KEY.length; i++) {
            final String p = (projected != null) ? projected[i] : "";
            final String u = (unitSymbol != null) ? unitSymbol[i] : "";
            final String g = (geographic != null) ? geographic[i] : "";
            if (!p.isEmpty() || !g.isEmpty()) {
                vocabulary.appendLabel(BOUND_KEY[i], table);
                nextColumn(table);
                table.append(CharSequences.spaces(maxProjLength - p.length())).append(p);
                table.append(CharSequences.spaces(maxUnitLength - u.length())).append(u).append(separator);
                table.append(CharSequences.spaces(maxGeogLength - g.length())).append(g);
                table.nextLine();
            }
        }
    }
    if (crs != null) {
        append(table, vocabulary, Vocabulary.Keys.CoordinateRefSys, IdentifiedObjects.getName(crs, null));
    }
    /*
         * Organization responsible for defining the characteristics of the location instance.
         */
    final AbstractParty administrator = location.getAdministrator();
    if (administrator != null) {
        append(table, vocabulary, Vocabulary.Keys.Administrator, toString(administrator.getName(), locale));
    }
    table.appendHorizontalSeparator();
    table.flush();
    if (warning != null) {
        vocabulary.appendLabel(Vocabulary.Keys.Warnings, toAppendTo);
        toAppendTo.append(warning.toString()).append(lineSeparator);
    }
}
Also used : Locale(java.util.Locale) Vocabulary(org.apache.sis.util.resources.Vocabulary) DirectPosition(org.opengis.geometry.DirectPosition) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) Extent(org.opengis.metadata.extent.Extent) FactoryException(org.opengis.util.FactoryException) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) UnitFormat(org.apache.sis.measure.UnitFormat) RoundingMode(java.math.RoundingMode) TableAppender(org.apache.sis.io.TableAppender) Latitude(org.apache.sis.measure.Latitude) GeographicBoundingBox(org.opengis.metadata.extent.GeographicBoundingBox) InternationalString(org.opengis.util.InternationalString) AngleFormat(org.apache.sis.measure.AngleFormat) Envelope(org.opengis.geometry.Envelope) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeographicCRS(org.opengis.referencing.crs.GeographicCRS) AbstractParty(org.apache.sis.metadata.iso.citation.AbstractParty) TransformException(org.opengis.referencing.operation.TransformException) Longitude(org.apache.sis.measure.Longitude) Date(java.util.Date) ParseException(java.text.ParseException) TransformException(org.opengis.referencing.operation.TransformException) IOException(java.io.IOException) FactoryException(org.opengis.util.FactoryException) Angle(org.apache.sis.measure.Angle) InternationalString(org.opengis.util.InternationalString) NumberFormat(java.text.NumberFormat)

Example 5 with Vocabulary

use of org.apache.sis.util.resources.Vocabulary in project sis by apache.

the class Warnings method toString.

/**
 * Returns a string representation of the warning messages in the given locale.
 * This method formats the warnings in a bullet list.
 *
 * @param  locale  the locale to use for formatting warning messages.
 * @return a string representation of the warning messages.
 */
public String toString(final Locale locale) {
    final StringBuilder buffer = new StringBuilder(250);
    final String lineSeparator = System.lineSeparator();
    final Messages resources = Messages.getResources(locale);
    buffer.append(resources.getString(isParsing ? Messages.Keys.IncompleteParsing_1 : Messages.Keys.NonConformFormatting_1, root));
    if (messages != null) {
        for (final Iterator<?> it = messages.iterator(); it.hasNext(); ) {
            final InternationalString i18n = (InternationalString) it.next();
            Exception cause = (Exception) it.next();
            final String message;
            if (i18n != null) {
                message = i18n.toString(locale);
            } else {
                /*
                     * If there is no message, then we must have at least an exception.
                     * Consequently a NullPointerException in following line would be a bug.
                     */
                final String[] sources = exceptionSources.get(cause);
                if (sources != null) {
                    message = Errors.getResources(locale).getString(Errors.Keys.UnparsableStringInElement_2, sources);
                } else {
                    message = cause.toString();
                    cause = null;
                }
            }
            buffer.append(lineSeparator).append(" • ").append(message);
            if (cause != null) {
                String details = Exceptions.getLocalizedMessage(cause, locale);
                if (details == null) {
                    details = cause.toString();
                }
                buffer.append(lineSeparator).append("   ").append(details);
            }
        }
    }
    /*
         * If the parser found some unknown elements, formats an enclosed bullet list for them.
         */
    if (!ignoredElements.isEmpty()) {
        final Vocabulary vocabulary = Vocabulary.getResources(locale);
        buffer.append(lineSeparator).append(" • ").append(resources.getString(Messages.Keys.UnknownElementsInText));
        for (final Map.Entry<String, List<String>> entry : ignoredElements.entrySet()) {
            buffer.append(lineSeparator).append("    ‣ ").append(vocabulary.getString(Vocabulary.Keys.Quoted_1, entry.getKey()));
            String separator = vocabulary.getString(Vocabulary.Keys.InBetweenWords);
            for (final String p : entry.getValue()) {
                buffer.append(separator).append(p);
                separator = ", ";
            }
            buffer.append('.');
        }
    }
    /*
         * There is intentionally line separator at the end of the last line, because the string returned by
         * this method is typically written or logged by a call to System.out.println(…) or something equivalent.
         * A trailing line separator cause a visual disruption in log records for instance.
         */
    return buffer.toString();
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) Messages(org.apache.sis.util.resources.Messages) InternationalString(org.opengis.util.InternationalString) ArrayList(java.util.ArrayList) List(java.util.List) InternationalString(org.opengis.util.InternationalString) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map)

Aggregations

Vocabulary (org.apache.sis.util.resources.Vocabulary)8 TableAppender (org.apache.sis.io.TableAppender)6 InternationalString (org.opengis.util.InternationalString)5 ArrayList (java.util.ArrayList)4 Format (java.text.Format)3 NumberFormat (java.text.NumberFormat)3 LinkedHashMap (java.util.LinkedHashMap)3 TabularFormat (org.apache.sis.io.TabularFormat)3 IdentifiedObject (org.opengis.referencing.IdentifiedObject)3 IOException (java.io.IOException)2 FieldPosition (java.text.FieldPosition)2 Map (java.util.Map)2 UncheckedIOException (java.io.UncheckedIOException)1 RoundingMode (java.math.RoundingMode)1 SQLException (java.sql.SQLException)1 DecimalFormat (java.text.DecimalFormat)1 ParseException (java.text.ParseException)1 Collection (java.util.Collection)1 Date (java.util.Date)1 Iterator (java.util.Iterator)1