Search in sources :

Example 6 with Vocabulary

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

the class ParameterFormat method format.

/**
 * Implementation of public {@code format(…)} methods for all content levels except {@code NAME_SUMMARY}.
 *
 * @param  name    the group name, usually {@code descriptor.getName().getCode()}.
 * @param  group   the parameter descriptor, usually {@code values.getDescriptor()}.
 * @param  values  the parameter values, or {@code null} if none.
 * @throws IOException if an error occurred while writing to the given appendable.
 */
private void format(final String name, final ParameterDescriptorGroup group, final ParameterValueGroup values, final Appendable out) throws IOException {
    final boolean isBrief = (contentLevel == ContentLevel.BRIEF);
    final boolean showObligation = !isBrief || (values == null);
    final boolean hasColors = (colors != null);
    final String lineSeparator = this.lineSeparator;
    final Map<String, Integer> remarks = new LinkedHashMap<>();
    final ParameterTableRow header = new ParameterTableRow(group, displayLocale, preferredCodespaces, remarks, isBrief);
    final String groupCodespace = header.getCodeSpace();
    /*
         * Prepares the informations to be printed later as table rows. We scan all rows before to print them
         * in order to compute the width of codespaces. During this process, we split the objects to be printed
         * later in two collections: simple parameters are stored as (descriptor,value) pairs, while groups are
         * stored in an other collection for deferred formatting after the simple parameters.
         */
    int codespaceWidth = 0;
    final Collection<?> elements = (values != null) ? values.values() : group.descriptors();
    final Map<GeneralParameterDescriptor, ParameterTableRow> descriptorValues = new LinkedHashMap<>(hashMapCapacity(elements.size()));
    // To be created only if needed (it is usually not).
    List<Object> deferredGroups = null;
    for (final Object element : elements) {
        final GeneralParameterValue parameter;
        final GeneralParameterDescriptor descriptor;
        if (values != null) {
            parameter = (GeneralParameterValue) element;
            descriptor = parameter.getDescriptor();
        } else {
            parameter = null;
            descriptor = (GeneralParameterDescriptor) element;
        }
        if (descriptor instanceof ParameterDescriptorGroup) {
            if (deferredGroups == null) {
                deferredGroups = new ArrayList<>(4);
            }
            deferredGroups.add(element);
            continue;
        }
        /*
             * In the vast majority of cases, there is only one value for each parameter. However
             * if we find more than one value, we will append all extra occurrences in a "multiple
             * values" list to be formatted in the same row.
             */
        Object value = null;
        Unit<?> unit = null;
        if (parameter instanceof ParameterValue<?>) {
            final ParameterValue<?> p = (ParameterValue<?>) parameter;
            value = p.getValue();
            unit = p.getUnit();
        } else if (descriptor instanceof ParameterDescriptor<?>) {
            final ParameterDescriptor<?> p = (ParameterDescriptor<?>) descriptor;
            value = p.getDefaultValue();
            unit = p.getUnit();
        }
        ParameterTableRow row = descriptorValues.get(descriptor);
        if (row == null) {
            row = new ParameterTableRow(descriptor, displayLocale, preferredCodespaces, remarks, isBrief);
            descriptorValues.put(descriptor, row);
            if (row.codespaceWidth > codespaceWidth) {
                codespaceWidth = row.codespaceWidth;
            }
        }
        row.addValue(value, unit);
    }
    /*
         * Finished to collect the values. Now transform the values:
         *
         *   - Singleton value of array types (either primitive or not) are wrapped into a list.
         *   - Values are formatted.
         *   - Value domains are formatted.
         *   - Position of the character on which to do the alignment are remembered.
         */
    int unitWidth = 0;
    int valueDomainAlignment = 0;
    boolean writeCodespaces = (groupCodespace == null);
    final StringBuffer buffer = new StringBuffer();
    final FieldPosition dummyFP = new FieldPosition(-1);
    for (final Map.Entry<GeneralParameterDescriptor, ParameterTableRow> entry : descriptorValues.entrySet()) {
        final GeneralParameterDescriptor descriptor = entry.getKey();
        if (descriptor instanceof ParameterDescriptor<?>) {
            final ParameterTableRow row = entry.getValue();
            /*
                 * Verify if all rows use the same codespace than the header, in which case we can omit
                 * row codespace formatting.
                 */
            if (!writeCodespaces && !groupCodespace.equals(entry.getValue().getCodeSpace())) {
                writeCodespaces = true;
            }
            /*
                 * Format the value domain, so we can compute the character position on which to perform alignment.
                 */
            final Range<?> valueDomain = Parameters.getValueDomain((ParameterDescriptor<?>) descriptor);
            if (valueDomain != null) {
                final int p = row.setValueDomain(valueDomain, getFormat(Range.class), buffer);
                if (p > valueDomainAlignment) {
                    valueDomainAlignment = p;
                }
            }
            /*
                 * Singleton array conversion. Because it may be an array of primitive types, we can not just
                 * cast to Object[]. Then formats the units, with a space before the unit if the symbol is a
                 * letter or digit (i.e. we do not put a space in front of ° symbol for instance).
                 */
            row.expandSingleton();
            final int length = row.units.size();
            for (int i = 0; i < length; i++) {
                final Object unit = row.units.get(i);
                if (unit != null) {
                    if (getFormat(Unit.class).format(unit, buffer, dummyFP).length() != 0) {
                        if (Character.isLetterOrDigit(buffer.codePointAt(0))) {
                            buffer.insert(0, ' ');
                        }
                    }
                    final String symbol = buffer.toString();
                    row.units.set(i, symbol);
                    buffer.setLength(0);
                    final int p = symbol.length();
                    if (p > unitWidth) {
                        unitWidth = p;
                    }
                }
            }
        }
    }
    /*
         * Finished to prepare information. Now begin the actual writing.
         * First, formats the table header (i.e. the column names).
         */
    final Vocabulary resources = Vocabulary.getResources(displayLocale);
    header.writeIdentifiers(out, true, colors, false, lineSeparator);
    out.append(lineSeparator);
    final char horizontalBorder = isBrief ? '─' : '═';
    final TableAppender table = (isBrief || !columnSeparator.equals(SEPARATOR)) ? new TableAppender(out, columnSeparator) : new TableAppender(out);
    table.setMultiLinesCells(true);
    table.nextLine(horizontalBorder);
    int numColumnsBeforeValue = 0;
    for (int i = 0; ; i++) {
        boolean end = false;
        final short key;
        switch(i) {
            case 0:
                {
                    key = Vocabulary.Keys.Name;
                    break;
                }
            case 1:
                {
                    key = Vocabulary.Keys.Type;
                    break;
                }
            case 2:
                {
                    if (!showObligation) {
                        continue;
                    }
                    key = Vocabulary.Keys.Obligation;
                    break;
                }
            case 3:
                {
                    key = Vocabulary.Keys.ValueDomain;
                    break;
                }
            case 4:
                {
                    key = (values == null) ? Vocabulary.Keys.DefaultValue : Vocabulary.Keys.Value;
                    end = true;
                    break;
                }
            default:
                throw new AssertionError(i);
        }
        if (hasColors)
            table.append(X364.BOLD.sequence());
        table.append(resources.getString(key));
        if (hasColors)
            table.append(X364.NORMAL.sequence());
        if (!writeCodespaces && i == 0) {
            table.append(" (").append(groupCodespace).append(')');
        }
        if (end)
            break;
        nextColumn(table);
        numColumnsBeforeValue++;
    }
    table.nextLine();
    /*
         * Now process to the formatting of (descriptor,value) pairs. Each descriptor's alias
         * will be formatted on its own line in a table row. If there is more than one value,
         * then each value will be formatted on its own line as well. Note that the values may
         * be null if there is none.
         */
    char horizontalLine = horizontalBorder;
    for (final Map.Entry<GeneralParameterDescriptor, ParameterTableRow> entry : descriptorValues.entrySet()) {
        if (horizontalLine != 0) {
            table.nextLine('─');
        }
        horizontalLine = isBrief ? 0 : '─';
        final ParameterTableRow row = entry.getValue();
        row.codespaceWidth = codespaceWidth;
        row.writeIdentifiers(table, writeCodespaces, null, hasColors, lineSeparator);
        nextColumn(table);
        final GeneralParameterDescriptor generalDescriptor = entry.getKey();
        if (generalDescriptor instanceof ParameterDescriptor<?>) {
            /*
                 * Writes value type.
                 */
            final ParameterDescriptor<?> descriptor = (ParameterDescriptor<?>) generalDescriptor;
            final Class<?> valueClass = descriptor.getValueClass();
            if (valueClass != null) {
                // Should never be null, but let be safe.
                table.append(getFormat(Class.class).format(valueClass, buffer, dummyFP).toString());
            }
            nextColumn(table);
            buffer.setLength(0);
            /*
                 * Writes the obligation (mandatory or optional).
                 */
            if (showObligation) {
                final int minimumOccurs = descriptor.getMinimumOccurs();
                final int maximumOccurs = descriptor.getMaximumOccurs();
                if (maximumOccurs == 1) {
                    table.append(resources.getString(minimumOccurs == 0 ? Vocabulary.Keys.Optional : Vocabulary.Keys.Mandatory));
                } else {
                    final Format f = getFormat(Integer.class);
                    table.append(f.format(minimumOccurs, buffer, dummyFP).toString()).append(" … ");
                    buffer.setLength(0);
                    if (maximumOccurs == Integer.MAX_VALUE) {
                        table.append('∞');
                    } else {
                        table.append(f.format(maximumOccurs, buffer, dummyFP).toString());
                        buffer.setLength(0);
                    }
                }
                nextColumn(table);
            }
            /*
                 * Writes minimum and maximum values, together with the unit of measurement (if any).
                 */
            final String valueDomain = row.valueDomain;
            if (valueDomain != null) {
                table.append(CharSequences.spaces(valueDomainAlignment - row.valueDomainAlignment)).append(valueDomain);
            }
            nextColumn(table);
            /*
                 * Writes the values, each on its own line, together with their unit of measurement.
                 */
            final byte alignment = Number.class.isAssignableFrom(valueClass) ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT;
            table.setCellAlignment(alignment);
            final int length = row.values.size();
            for (int i = 0; i < length; i++) {
                Object value = row.values.get(i);
                if (value != null) {
                    if (i != 0) {
                        /*
                             * If the same parameter is repeated more than once (not allowed by ISO 19111,
                             * but this extra flexibility is allowed by Apache SIS), write the ditto mark
                             * in all previous columns (name, type, etc.) on a new row.
                             */
                        final String ditto = resources.getString(Vocabulary.Keys.DittoMark);
                        table.nextLine();
                        table.setCellAlignment(TableAppender.ALIGN_CENTER);
                        for (int j = 0; j < numColumnsBeforeValue; j++) {
                            table.append(ditto);
                            nextColumn(table);
                        }
                        table.setCellAlignment(alignment);
                    }
                    /*
                         * Format the value followed by the unit of measure, or followed by spaces if there is no unit
                         * for this value. The intent is the right align the numerical value rather than the numerical
                         * + unit tupple.
                         */
                    final Format format = getFormat(value.getClass());
                    if (format != null) {
                        if (format instanceof NumberFormat && value instanceof Number) {
                            configure((NumberFormat) format, Math.abs(((Number) value).doubleValue()));
                        }
                        value = format.format(value, buffer, dummyFP);
                    }
                    table.append(value.toString());
                    buffer.setLength(0);
                    int pad = unitWidth;
                    final String unit = (String) row.units.get(i);
                    if (unit != null) {
                        table.append(unit);
                        pad -= unit.length();
                    }
                    table.append(CharSequences.spaces(pad));
                }
            }
        }
        table.nextLine();
        table.setCellAlignment(TableAppender.ALIGN_LEFT);
    }
    table.nextLine(horizontalBorder);
    table.flush();
    /*
         * Write remarks, if any.
         */
    for (final Map.Entry<String, Integer> remark : remarks.entrySet()) {
        ParameterTableRow.writeFootnoteNumber(out, remark.getValue());
        out.append(' ').append(remark.getKey()).append(lineSeparator);
    }
    /*
         * Now formats all groups deferred to the end of this table, with recursive calls to
         * this method (recursive calls use their own TableWriter instance, so they may result
         * in a different cell layout). Most of the time, there is no such additional 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.append(lineSeparator);
            format(name + '/' + descriptor.getName().getCode(), descriptor, value, out);
        }
    }
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) TableAppender(org.apache.sis.io.TableAppender) Unit(javax.measure.Unit) LinkedHashMap(java.util.LinkedHashMap) Format(java.text.Format) NumberFormat(java.text.NumberFormat) TabularFormat(org.apache.sis.io.TabularFormat) FieldPosition(java.text.FieldPosition) Range(org.apache.sis.measure.Range) IdentifiedObject(org.opengis.referencing.IdentifiedObject) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) NumberFormat(java.text.NumberFormat)

Example 7 with Vocabulary

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

the class StatisticsFormat method format.

/**
 * Formats the given statistics in a tabular format. This method does not check
 * for the statistics on {@linkplain Statistics#differences() differences} - if
 * such statistics are wanted, they must be included in the given array.
 *
 * @param  stats       the statistics to format.
 * @param  toAppendTo  where to format the statistics.
 * @throws IOException if an error occurred while writing to the given appendable.
 */
public void format(final Statistics[] stats, final Appendable toAppendTo) throws IOException {
    /*
         * Inspect the given statistics in order to determine if we shall omit the headers,
         * and if we shall omit the count of NaN values.
         */
    final String[] headers = new String[stats.length];
    boolean showHeaders = false;
    boolean showNaNCount = false;
    for (int i = 0; i < stats.length; i++) {
        final Statistics s = stats[i];
        showNaNCount |= (s.countNaN() != 0);
        final InternationalString header = s.name();
        if (header != null) {
            headers[i] = header.toString(headerLocale);
            showHeaders |= (headers[i] != null);
        }
    }
    char horizontalLine = 0;
    String separator = columnSeparator;
    switch(borderWidth) {
        case 1:
            horizontalLine = '─';
            separator += "│ ";
            break;
        case 2:
            horizontalLine = '═';
            separator += "║ ";
            break;
    }
    final TableAppender table = new TableAppender(toAppendTo, separator);
    final Vocabulary resources = Vocabulary.getResources(headerLocale);
    /*
         * If there is a header for at least one statistics, write the full headers row.
         */
    if (horizontalLine != 0) {
        table.nextLine(horizontalLine);
    }
    if (showHeaders) {
        table.nextColumn();
        for (final String header : headers) {
            if (header != null) {
                table.append(header);
                table.setCellAlignment(TableAppender.ALIGN_CENTER);
            }
            table.nextColumn();
        }
        table.append(lineSeparator);
        if (horizontalLine != 0) {
            table.nextLine(horizontalLine);
        }
    }
    /*
         * Initialize the NumberFormat for formatting integers without scientific notation.
         * This is necessary since the format may have been modified by a previous execution
         * of this method.
         */
    final Format format = getFormat(Double.class);
    if (format instanceof DecimalFormat) {
        // Also disable scientific notation.
        ((DecimalFormat) format).applyPattern("#0");
    } else if (format instanceof NumberFormat) {
        setFractionDigits((NumberFormat) format, 0);
    }
    /*
         * Iterates over the rows to format (count, minimum, maximum, mean, RMS, standard deviation),
         * then iterate over columns (statistics on sample values, on the first derivatives, etc.)
         * The NumberFormat configuration may be different for each column, but we can skip many
         * reconfiguration in the common case where there is only one column.
         */
    boolean needsConfigure = false;
    for (int i = 0; i < KEYS.length; i++) {
        switch(i) {
            case 1:
                if (!showNaNCount)
                    continue;
                else
                    break;
            // Case 3 and others need reconfiguration only if there is more than one column.
            case 2:
                needsConfigure = true;
                break;
            case 3:
                needsConfigure = (stats[0].differences() != null);
                break;
        }
        table.setCellAlignment(TableAppender.ALIGN_LEFT);
        table.append(resources.getString(KEYS[i])).append(':');
        for (final Statistics s : stats) {
            final Number value;
            switch(i) {
                case 0:
                    value = s.count();
                    break;
                case 1:
                    value = s.countNaN();
                    break;
                case 2:
                    value = s.minimum();
                    break;
                case 3:
                    value = s.maximum();
                    break;
                case 4:
                    value = s.mean();
                    break;
                case 5:
                    value = s.rms();
                    break;
                case 6:
                    value = s.standardDeviation(allPopulation);
                    break;
                default:
                    throw new AssertionError(i);
            }
            if (needsConfigure) {
                configure(format, s);
            }
            table.append(beforeFill);
            table.nextColumn(fillCharacter);
            table.append(format.format(value));
            table.setCellAlignment(TableAppender.ALIGN_RIGHT);
        }
        table.append(lineSeparator);
    }
    if (horizontalLine != 0) {
        table.nextLine(horizontalLine);
    }
    /*
         * TableAppender needs to be explicitly flushed in order to format the values.
         */
    table.flush();
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) DecimalFormat(java.text.DecimalFormat) TableAppender(org.apache.sis.io.TableAppender) InternationalString(org.opengis.util.InternationalString) Format(java.text.Format) DecimalFormat(java.text.DecimalFormat) TabularFormat(org.apache.sis.io.TabularFormat) NumberFormat(java.text.NumberFormat) InternationalString(org.opengis.util.InternationalString) NumberFormat(java.text.NumberFormat)

Example 8 with Vocabulary

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

the class Exceptions method formatChainedMessages.

/**
 * Returns a string which contain the given message on the first line, followed by the
 * {@linkplain #getLocalizedMessage(Throwable, Locale) localized message} of the given exception
 * on the next line. If the exception has a {@linkplain Throwable#getCause() causes}, then
 * the class name and the localized message of the cause are formatted on the next line
 * and the process is repeated for the whole cause chain, omitting duplicated messages.
 *
 * <p>{@link SQLException} is handled especially in order to process the
 * {@linkplain SQLException#getNextException() next exception} instead than the cause.</p>
 *
 * <p>This method does not format the stack trace.</p>
 *
 * @param  locale  the preferred locale for the exception message, or {@code null}.
 * @param  header  the message to insert on the first line, or {@code null} if none.
 * @param  cause   the exception, or {@code null} if none.
 * @return the formatted message, or {@code null} if both the header was {@code null}
 *         and no exception provide a message.
 */
public static String formatChainedMessages(final Locale locale, final String header, Throwable cause) {
    final List<String> previousLines = new ArrayList<>();
    StringBuilder buffer = null;
    Vocabulary resources = null;
    while (cause != null) {
        final String message = CharSequences.trimWhitespaces(getLocalizedMessage(cause, locale));
        if (message != null && !message.isEmpty()) {
            if (buffer == null) {
                buffer = new StringBuilder(128);
                if (header != null) {
                    final int length = CharSequences.skipTrailingWhitespaces(header, 0, header.length());
                    if (length > 0) {
                        buffer.append(header, 0, length);
                    }
                    previousLines.add(header);
                }
            }
            if (!contains(previousLines, message)) {
                previousLines.add(message);
                if (buffer.length() != 0) {
                    if (resources == null) {
                        resources = Vocabulary.getResources(locale);
                    }
                    buffer.append(System.lineSeparator()).append(resources.getString(Vocabulary.Keys.CausedBy_1, cause.getClass())).append(": ");
                }
                buffer.append(message);
            }
        }
        if (cause instanceof SQLException) {
            final SQLException next = ((SQLException) cause).getNextException();
            if (next != null) {
                cause = next;
                continue;
            }
        }
        cause = cause.getCause();
    }
    return (buffer != null) ? buffer.toString() : header;
}
Also used : Vocabulary(org.apache.sis.util.resources.Vocabulary) SQLException(java.sql.SQLException) ArrayList(java.util.ArrayList) InternationalString(org.opengis.util.InternationalString)

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