Search in sources :

Example 1 with Deprecable

use of org.apache.sis.util.Deprecable in project sis by apache.

the class CoordinateOperationRegistry method search.

/**
 * Returns operations for conversions or transformations between two coordinate reference systems.
 * This method extracts the authority code from the supplied {@code sourceCRS} and {@code targetCRS},
 * and submit them to the {@link #registry}. If no operation is found for those codes, then this method
 * returns {@code null}.
 *
 * @param  sourceCRS  source coordinate reference system.
 * @param  targetCRS  target coordinate reference system.
 * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}, or {@code null}
 *         if no such operation is explicitly defined in the underlying database.
 * @throws IllegalArgumentException if the coordinate systems are not of the same type or axes do not match.
 * @throws IncommensurableException if the units are not compatible or a unit conversion is non-linear.
 * @throws FactoryException if an error occurred while creating the operation.
 */
private List<CoordinateOperation> search(final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS) throws IllegalArgumentException, IncommensurableException, FactoryException {
    final List<String> sources = findCode(sourceCRS);
    if (sources.isEmpty())
        return null;
    final List<String> targets = findCode(targetCRS);
    if (targets.isEmpty())
        return null;
    final List<CoordinateOperation> operations = new ArrayList<>();
    boolean foundDirectOperations = false;
    boolean useDeprecatedOperations = false;
    for (final String sourceID : sources) {
        for (final String targetID : targets) {
            if (sourceID.equals(targetID)) {
                /*
                     * Above check is necessary because this method may be invoked in some situations where the code
                     * are equal while the CRS are not. Such situation should be illegal, but unfortunately it still
                     * happen because many software products are not compliant with EPSG definition of axis order.
                     * In such cases we will need to compute a transform from sourceCRS to targetCRS ignoring the
                     * source and target codes. The CoordinateOperationFinder class can do that, providing that we
                     * prevent this CoordinateOperationRegistry to (legitimately) claims that the operation from
                     * sourceCode to targetCode is the identity transform.
                     */
                return null;
            }
            /*
                 * Some pairs of CRS have a lot of coordinate operations backed by datum shift grids.
                 * We do not want to load all of them until we found the right coordinate operation.
                 * The non-public Semaphores.METADATA_ONLY mechanism instructs EPSGDataAccess to
                 * instantiate DeferredCoordinateOperation instead of full coordinate operations.
                 */
            final boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
            try {
                Collection<CoordinateOperation> authoritatives;
                try {
                    authoritatives = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
                    final boolean inverse = Containers.isNullOrEmpty(authoritatives);
                    if (inverse) {
                        /*
                             * No operation from 'source' to 'target' available. But maybe there is an inverse operation.
                             * This is typically the case when the user wants to convert from a projected to a geographic CRS.
                             * The EPSG database usually contains transformation paths for geographic to projected CRS only.
                             */
                        if (foundDirectOperations) {
                            // Ignore inverse operations if we already have direct ones.
                            continue;
                        }
                        authoritatives = registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
                        if (Containers.isNullOrEmpty(authoritatives)) {
                            continue;
                        }
                    } else if (!foundDirectOperations) {
                        foundDirectOperations = true;
                        // Keep only direct operations.
                        operations.clear();
                    }
                } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException e) {
                    /*
                         * sourceCode or targetCode is unknown to the underlying authority factory.
                         * Ignores the exception and fallback on the generic algorithm provided by
                         * CoordinateOperationFinder.
                         */
                    log(null, e);
                    continue;
                }
                /*
                     * If we found at least one non-deprecated operation, we will stop the search at
                     * the first deprecated one (assuming that deprecated operations are sorted last).
                     * Deprecated operations are kept only if there is no non-deprecated operations.
                     */
                try {
                    for (final CoordinateOperation candidate : authoritatives) {
                        if (candidate != null) {
                            // Paranoiac check.
                            if ((candidate instanceof Deprecable) && ((Deprecable) candidate).isDeprecated()) {
                                if (!useDeprecatedOperations && !operations.isEmpty())
                                    break;
                                useDeprecatedOperations = true;
                            } else if (useDeprecatedOperations) {
                                useDeprecatedOperations = false;
                                // Replace deprecated operations by non-deprecated ones.
                                operations.clear();
                            }
                            operations.add(candidate);
                        }
                    }
                } catch (BackingStoreException exception) {
                    throw exception.unwrapOrRethrow(FactoryException.class);
                }
            } finally {
                if (!mdOnly) {
                    Semaphores.clear(Semaphores.METADATA_ONLY);
                }
            }
        }
    }
    /*
         * At this point we got the list of coordinate operations. Now, sort them in preference order.
         * We will loop over all coordinate operations and select the one having the largest intersection
         * with the area of interest. Note that if the user did not specified an area of interest himself,
         * then we need to get one from the CRS. This is necessary for preventing the transformation from
         * NAD27 to NAD83 in Idaho to select the transform for Alaska (since the later has a larger area).
         */
    CoordinateOperationSorter.sort(operations, Extents.getGeographicBoundingBox(areaOfInterest));
    final ListIterator<CoordinateOperation> it = operations.listIterator();
    while (it.hasNext()) {
        /*
             * At this point we filtered a CoordinateOperation by looking only at its metadata.
             * Code following this point will need the full coordinate operation, including its
             * MathTransform. So if we got a deferred operation, we need to resolve it now.
             * Conversely, we should not use metadata below this point because the call to
             * inverse(CoordinateOperation) is not guaranteed to preserve all metadata.
             */
        CoordinateOperation operation = it.next();
        try {
            if (operation instanceof DeferredCoordinateOperation) {
                operation = ((DeferredCoordinateOperation) operation).create();
            }
            if (operation instanceof SingleOperation && operation.getMathTransform() == null) {
                operation = fromDefiningConversion((SingleOperation) operation, foundDirectOperations ? sourceCRS : targetCRS, foundDirectOperations ? targetCRS : sourceCRS);
                if (operation == null) {
                    it.remove();
                    continue;
                }
            }
            if (!foundDirectOperations) {
                operation = inverse(operation);
            }
        } catch (NoninvertibleTransformException | MissingFactoryResourceException e) {
            /*
                 * If we failed to get the real CoordinateOperation instance, remove it from
                 * the collection and try again in order to get the next best choices.
                 */
            log(null, e);
            it.remove();
            // Try again with the next best case.
            continue;
        }
        /*
             * It is possible that the CRS given to this method were not quite right.  For example the user
             * may have created his CRS from a WKT using a different axis order than the order specified by
             * the authority and still (wrongly) call those CRS "EPSG:xxxx".  So we check if the source and
             * target CRS for the operation we just created are equivalent to the CRS specified by the user.
             *
             * NOTE: FactoryException may be thrown if we fail to create a transform from the user-provided
             * CRS to the authority-provided CRS. That transform should have been only an identity transform,
             * or a simple affine transform if the user specified wrong CRS as explained in above paragraph.
             * If we fail here, we are likely to fail for all other transforms. So we are better to let the
             * FactoryException propagate.
             */
        operation = complete(operation, sourceCRS, targetCRS);
        if (filter(operation)) {
            if (stopAtFirst) {
                operations.clear();
                operations.add(operation);
                break;
            }
            it.set(operation);
        } else {
            it.remove();
        }
    }
    return operations;
}
Also used : NoSuchAuthorityCodeException(org.opengis.referencing.NoSuchAuthorityCodeException) FactoryException(org.opengis.util.FactoryException) NoSuchAuthorityFactoryException(org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException) ArrayList(java.util.ArrayList) BackingStoreException(org.apache.sis.util.collection.BackingStoreException) DeferredCoordinateOperation(org.apache.sis.internal.referencing.DeferredCoordinateOperation) DeferredCoordinateOperation(org.apache.sis.internal.referencing.DeferredCoordinateOperation) MissingFactoryResourceException(org.apache.sis.referencing.factory.MissingFactoryResourceException) Deprecable(org.apache.sis.util.Deprecable)

Example 2 with Deprecable

use of org.apache.sis.util.Deprecable in project sis by apache.

the class InverseOperationMethod method create.

/**
 * Returns or create the inverse of the given operation method. If the same operation method can be used
 * for the inverse operation either with the exact same parameter values or with the sign of some values
 * reversed, then the given method is returned as-is. Otherwise a synthetic method is created.
 */
static OperationMethod create(final OperationMethod method) {
    if (method instanceof InverseOperationMethod) {
        return ((InverseOperationMethod) method).inverse;
    }
    if (!isInvertible(method)) {
        boolean useSameParameters = false;
        for (final GeneralParameterDescriptor descriptor : method.getParameters().descriptors()) {
            useSameParameters = (descriptor.getRemarks() instanceof SignReversalComment);
            if (!useSameParameters)
                break;
        }
        if (!useSameParameters) {
            Identifier name = method.getName();
            name = new ImmutableIdentifier(null, null, "Inverse of " + name.getCode());
            final Map<String, Object> properties = new HashMap<>(6);
            properties.put(NAME_KEY, name);
            properties.put(FORMULA_KEY, method.getFormula());
            properties.put(REMARKS_KEY, method.getRemarks());
            if (method instanceof Deprecable) {
                properties.put(DEPRECATED_KEY, ((Deprecable) method).isDeprecated());
            }
            return new InverseOperationMethod(properties, method);
        }
    }
    return method;
}
Also used : ImmutableIdentifier(org.apache.sis.metadata.iso.ImmutableIdentifier) Identifier(org.opengis.metadata.Identifier) HashMap(java.util.HashMap) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) SignReversalComment(org.apache.sis.internal.referencing.SignReversalComment) InternationalString(org.opengis.util.InternationalString) ImmutableIdentifier(org.apache.sis.metadata.iso.ImmutableIdentifier) Deprecable(org.apache.sis.util.Deprecable)

Example 3 with Deprecable

use of org.apache.sis.util.Deprecable 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)

Aggregations

Deprecable (org.apache.sis.util.Deprecable)3 ArrayList (java.util.ArrayList)2 InternationalString (org.opengis.util.InternationalString)2 IOException (java.io.IOException)1 UncheckedIOException (java.io.UncheckedIOException)1 FieldPosition (java.text.FieldPosition)1 Format (java.text.Format)1 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 Iterator (java.util.Iterator)1 DeferredCoordinateOperation (org.apache.sis.internal.referencing.DeferredCoordinateOperation)1 SignReversalComment (org.apache.sis.internal.referencing.SignReversalComment)1 TableAppender (org.apache.sis.io.TableAppender)1 TabularFormat (org.apache.sis.io.TabularFormat)1 ImmutableIdentifier (org.apache.sis.metadata.iso.ImmutableIdentifier)1 MissingFactoryResourceException (org.apache.sis.referencing.factory.MissingFactoryResourceException)1 NoSuchAuthorityFactoryException (org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException)1 BackingStoreException (org.apache.sis.util.collection.BackingStoreException)1 Vocabulary (org.apache.sis.util.resources.Vocabulary)1 Identifier (org.opengis.metadata.Identifier)1