org.geotoolkit.io.wkt.Formatter Maven / Gradle / Ivy
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*/
package org.geotoolkit.io.wkt;
import java.util.Locale;
import java.util.Collection;
import java.text.NumberFormat;
import java.text.FieldPosition;
import java.lang.reflect.Array;
import javax.measure.unit.SI;
import javax.measure.unit.NonSI;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Length;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.datum.Datum;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.OperationMethod;
import org.opengis.util.CodeList;
import org.geotoolkit.io.X364;
import org.geotoolkit.lang.Debug;
import org.geotoolkit.lang.Visitor;
import org.geotoolkit.measure.Units;
import org.geotoolkit.util.Strings;
import org.geotoolkit.util.ArgumentChecks;
import org.geotoolkit.util.converter.Numbers;
import org.geotoolkit.internal.InternalUtilities;
import org.geotoolkit.internal.Citations;
import org.geotoolkit.referencing.IdentifiedObjects;
import org.geotoolkit.resources.Errors;
/**
* Formats {@linkplain FormattableObject formattable objects} as Well Known Text
* (WKT). A formatter is constructed with a specified set of {@linkplain Symbols symbols}.
* The {@linkplain Locale locale} associated with the symbols is used for querying
* {@linkplain Citation#getTitle() authority titles}.
*
* For example in order to format an object with {@linkplain Symbols#CURLY_BRACKETS curly brackets}
* instead of square ones and the whole text on the same line (no indentation), use the following:
*
* {@preformat java
* Formatter formatter = new Formatter(Symbols.CURLY_BRACKETS, null, WKTFormat.SINGLE_LINE);
* formatter.append(theObject);
* String wkt = formatter.toString();
*
* // Following is needed only if you want to reuse
* // the formatter again for other objects.
* formatter.clear();
* }
*
* Formatters are not synchronized. It is recommended to create separate formatter instances for
* each thread. If multiple threads access a formatter concurrently, it must be synchronized
* externally.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.20
*
* @since 2.0
* @level advanced
* @module
*/
@Visitor(Formattable.class)
public class Formatter {
/**
* Do not format an {@code "AUTHORITY"} element for instances of those classes.
*
* @see #isAuthorityAllowed
*/
@SuppressWarnings({"unchecked","rawtypes"})
private static final Class extends IdentifiedObject>[] AUTHORITY_EXCLUDES = new Class[] {
CoordinateSystemAxis.class
};
/**
* The symbols to use for this formatter.
*
* @see WKTFormat#getSymbols
* @see WKTFormat#setSymbols
*/
private final Symbols symbols;
/**
* The colors to use for this formatter, or {@code null} for no syntax coloring.
* If non-null, the terminal must be ANSI X3.64 compatible. The default value is
* {@code null}.
*
* @see WKTFormat#getColors
* @see WKTFormat#setColors
*/
Colors colors;
/**
* The preferred convention for objects or parameter names.
* This field should never be {@code null}.
*
* @see WKTFormat#getConvention()
* @see WKTFormat#setConvention(Convention)
*/
private Convention convention = Convention.OGC;
/**
* The preferred authority for objects or parameter names.
*
* @see WKTFormat#getAuthority
* @see WKTFormat#setAuthority
*/
private Citation authority;
/**
* The unit for formatting measures, or {@code null} for the "natural" unit of each WKT
* element.
*/
private Unit linearUnit;
/**
* The unit for formatting measures, or {@code null} for the "natural" unit of each WKT
* element. This value is set for example by {@code "GEOGCS"}, which force its enclosing
* {@code "PRIMEM"} to take the same units than itself.
*/
private Unit angularUnit;
/**
* The object to use for formatting numbers.
*/
private final NumberFormat numberFormat;
/**
* The object to use for formatting units.
*/
private final UnitFormat unitFormat;
/**
* Dummy field position.
*/
private final FieldPosition dummy = new FieldPosition(0);
/**
* The buffer in which to format. Consider this field as private and final. The only method to
* change the value of this field is {@link WKTFormat#format(Object, StringBuffer, FieldPosition)}.
*/
StringBuffer buffer;
/**
* The starting point in the buffer. Always 0, except when used by
* {@link WKTFormat#format(Object, StringBuffer, FieldPosition)}.
*/
int bufferBase;
/**
* The amount of spaces to use in indentation, or
* {@value org.geotoolkit.io.wkt.WKTFormat#SINGLE_LINE} if indentation is disabled.
*/
int indentation;
/**
* The amount of space to write on the left side of each line. This amount is increased
* by {@code indentation} every time a {@link FormattableObject} is appended in a new
* indentation level.
*/
private int margin;
/**
* The line separator to use.
*/
private final String lineSeparator = System.getProperty("line.separator", "\n");
/**
* {@code true} if a new line were requested during the execution of
* {@link #append(Formattable)}. This is used to determine if {@code UNIT}
* and {@code AUTHORITY} elements should appears on a new line too.
*/
private boolean lineChanged;
/**
* {@code true} if the WKT is invalid. Similar to {@link #unformattable}, except that
* this field is reset to {@code false} after the invalid part has been processed by
* {@link #append(Formattable)}. This field is for internal use only.
*/
private boolean invalidWKT;
/**
* Non-null if the WKT is invalid. If non-null, then this field contains the interface class
* of the problematic part (e.g. {@link org.opengis.referencing.crs.EngineeringCRS}).
*/
private Class> unformattable;
/**
* Warning that may be produced during WKT formatting, or {@code null} if none.
*/
String warning;
/**
* Creates a new instance of the formatter with the default symbols, no syntax coloring
* and the {@linkplain WKTFormat#getDefaultIndentation() default indentation}.
*/
public Formatter() {
this(FormattableObject.defaultIndentation);
}
/**
* Creates a new instance of the formatter with the default symbols, no syntax coloring
* and the given indentation.
*
* @param indentation The amount of spaces to use in indentation for WKT formatting,
* or {@value org.geotoolkit.io.wkt.WKTFormat#SINGLE_LINE} for formatting the
* whole WKT on a single line.
*
* @since 3.20
*/
public Formatter(final int indentation) {
this(Symbols.DEFAULT, null, indentation);
}
/**
* Creates a new instance of the formatter with the specified indentation.
* The WKT can be formatted on many lines, and the indentation will have
* the value specified to this constructor. If the specified indentation
* is {@value org.geotoolkit.io.wkt.WKTFormat#SINGLE_LINE}, then the
* whole WKT will be formatted on a single line.
*
* @param symbols The symbols.
* @param colors The syntax coloring, or {@code null} if none.
* @param indentation The amount of spaces to use in indentation for WKT formatting,
* or {@value org.geotoolkit.io.wkt.WKTFormat#SINGLE_LINE} for formatting the
* whole WKT on a single line.
*
* @since 3.00
*/
public Formatter(final Symbols symbols, final Colors colors, final int indentation) {
ArgumentChecks.ensureNonNull("symbols", symbols);
if (indentation < WKTFormat.SINGLE_LINE) {
throw new IllegalArgumentException(Errors.format(
Errors.Keys.ILLEGAL_ARGUMENT_$2, "indentation", indentation));
}
this.symbols = symbols;
this.colors = colors;
this.indentation = indentation;
this.numberFormat = (NumberFormat) symbols.numberFormat.clone();
this.unitFormat = UnitFormat.getInstance(symbols.locale);
this.buffer = new StringBuffer();
}
/**
* Constructor for private use by {@link WKTFormat#format} only.
* This constructor help to share some objects with {@link Parser}.
*/
Formatter(final Symbols symbols, final NumberFormat numberFormat) {
this.symbols = symbols;
this.indentation = FormattableObject.defaultIndentation;
this.numberFormat = numberFormat; // No clone needed.
this.unitFormat = UnitFormat.getInstance(symbols.locale);
// Do not set the buffer. It will be set by WKTFormat.format(...).
}
/**
* Sets the convention and the authority to use for formatting WKT elements.
*
* @param convention The convention, or {@code null} for the default value.
* @param authority The authority, or {@code null} for inferring it from the convention.
*/
final void setConvention(Convention convention, final Citation authority) {
if (convention == null) {
convention = Convention.forCitation(authority, Convention.OGC);
}
this.convention = convention;
this.authority = authority; // Will be inferred when first needed.
}
/**
* Sets the color in the {@linkplain #buffer} using the specified ANSI escape.
* The color is ignored unless syntax coloring has been explicitly enabled.
*/
private void setColor(final Colors.Element color) {
if (colors != null) {
final X364 c = colors.get(color);
if (c != null) {
buffer.append(c.sequence());
}
}
}
/**
* Resets the color in the {@linkplain #buffer} to the default. This method
* does nothing unless syntax coloring has been explicitly enabled.
*/
private void resetColor() {
if (colors != null) {
buffer.append(X364.FOREGROUND_DEFAULT.sequence());
}
}
/**
* Returns the color to use for the name of the specified object.
*/
private static Colors.Element getNameColor(final IdentifiedObject object) {
if (object instanceof Datum) {
return Colors.Element.DATUM;
}
if (object instanceof OperationMethod) {
return Colors.Element.METHOD;
}
if (object instanceof CoordinateSystemAxis) {
return Colors.Element.AXIS;
}
// Note: we can't test for MathTransform here, since it is not an IdentifiedObject.
// If we want to provide a color for the MathTransform name, we would need to
// do that in 'append(String)' method, but the later is for general string...
return null;
}
/**
* Appends a separator to the buffer, if needed.
*
* @param newLine If {@code true}, add a line separator too.
*/
private void appendSeparator(final boolean newLine) {
final Symbols symbols = this.symbols;
final StringBuffer buffer = this.buffer;
int length = buffer.length();
char c;
do {
if (length == bufferBase) {
return;
}
c = buffer.charAt(--length);
if (c == symbols.open || c == symbols.openArray) {
return;
}
} while (Character.isWhitespace(c) || c == symbols.space);
buffer.append(symbols.separator).append(symbols.space);
if (newLine && indentation > WKTFormat.SINGLE_LINE) {
buffer.append(lineSeparator).append(Strings.spaces(margin));
lineChanged = true;
}
}
/**
* Appends the given {@code Formattable} object. This method will automatically append
* the keyword (e.g. {@code "GEOCS"}), the name and the authority code, and will invokes
* formattable.{@linkplain FormattableObject#formatWKT(Formatter) formatWKT}(this)
* for completing the inner part of the WKT.
*
* @param formattable The formattable object to append to the WKT.
*/
public void append(final Formattable formattable) {
final Symbols symbols = this.symbols;
final StringBuffer buffer = this.buffer;
/*
* Formats the opening bracket and the object name (e.g. "NAD27").
* The WKT entity name (e.g. "PROJCS") will be formatted later.
* The result of this code portion looks like the following:
*
* ,
* ["NAD27 / Idaho Central"
*/
appendSeparator(true);
int base = buffer.length();
buffer.append(symbols.open);
final IdentifiedObject info = (formattable instanceof IdentifiedObject) ?
(IdentifiedObject) formattable : null;
if (info != null) {
final Colors.Element c = getNameColor(info);
if (c != null) {
setColor(c);
}
buffer.append(symbols.quote).append(getName(info)).append(symbols.quote);
if (c != null) {
resetColor();
}
}
/*
* Formats the part after the object name, then insert the WKT element name
* in front of them. The result of this code portion looks like the following:
*
* ,
* PROJCS["NAD27 / Idaho Central",
* GEOGCS[...etc...],
* ...etc...
*/
indent(+1);
lineChanged = false;
String keyword = formattable.formatWKT(this);
if (colors != null && invalidWKT) {
invalidWKT = false;
final X364 c = colors.get(Colors.Element.ERROR);
if (c != null) {
final String sequence = c.sequence();
buffer.insert(base, sequence + X364.BACKGROUND_DEFAULT.sequence());
base += sequence.length();
}
}
buffer.insert(base, keyword);
/*
* Formats the AUTHORITY[,] entity, if there is one. The entity
* will be on the same line than the enclosing one if no line separator were
* added (e.g. SPHEROID["Clarke 1866", ..., AUTHORITY["EPSG","7008"]]), or on
* a new line otherwise. After this block, the result looks like the following:
*
* ,
* PROJCS["NAD27 / Idaho Central",
* GEOGCS[...etc...],
* ...etc...
* AUTHORITY["EPSG","26769"]]
*/
final Identifier identifier = getIdentifier(info);
if (identifier!=null && isAuthorityAllowed(info)) {
final Citation authority = identifier.getAuthority();
if (authority != null) {
final String title = Citations.getIdentifier(authority);
if (title != null) {
appendSeparator(lineChanged);
buffer.append("AUTHORITY")
.append(symbols.open)
.append(symbols.quote)
.append(title)
.append(symbols.quote);
final String code = identifier.getCode();
if (code != null) {
buffer.append(symbols.separator)
.append(symbols.quote)
.append(code)
.append(symbols.quote);
}
buffer.append(symbols.close);
}
}
}
buffer.append(symbols.close);
lineChanged = true;
indent(-1);
}
/**
* Appends the specified OpenGIS's {@code IdentifiedObject} object.
*
* @param info The info object to append to the WKT.
*/
public void append(final IdentifiedObject info) {
if (info instanceof Formattable) {
append((Formattable) info);
} else {
throw unsupported(info);
}
}
/**
* Appends the specified math transform.
*
* @param transform The transform object to append to the WKT.
*/
public void append(final MathTransform transform) {
if (transform instanceof Formattable) {
append((Formattable) transform);
} else {
throw unsupported(transform);
}
}
/**
* Invoked when an object is not a supported implementation.
*
* @param object The object of unknown type.
* @return The exception to be thrown.
*/
private static UnformattableObjectException unsupported(final Object object) {
final Class> type = object.getClass();
return new UnformattableObjectException(Errors.format(
Errors.Keys.ILLEGAL_CLASS_$2, type, Formattable.class), type);
}
/**
* Appends a code list.
*
* @param code The code list to append to the WKT.
*/
public void append(final CodeList> code) {
if (code != null) {
appendSeparator(false);
setColor(Colors.Element.CODE_LIST);
final String name = code.name();
final boolean needQuotes = (name.indexOf(' ') >= 0);
final Symbols symbols = this.symbols;
final StringBuffer buffer = this.buffer;
if (needQuotes) {
buffer.append(symbols.quote);
}
buffer.append(name);
if (needQuotes) {
buffer.append(symbols.quote);
setInvalidWKT(code.getClass());
}
resetColor();
}
}
/**
* Appends a {@linkplain ParameterValue parameter} in WKT form. If the supplied parameter
* is actually a {@linkplain ParameterValueGroup parameter group}, all contained parameters
* will flattened in a single list.
*
* @param parameter The parameter to append to the WKT.
*/
public void append(final GeneralParameterValue parameter) {
if (parameter instanceof ParameterValueGroup) {
for (final GeneralParameterValue param : ((ParameterValueGroup)parameter).values()) {
append(param);
}
}
if (parameter instanceof ParameterValue>) {
final ParameterValue> param = (ParameterValue>) parameter;
final ParameterDescriptor> descriptor = param.getDescriptor();
final Unit> valueUnit = descriptor.getUnit();
Unit> unit = valueUnit;
if (unit!=null && !Unit.ONE.equals(unit)) {
Unit> contextUnit = linearUnit;
if (contextUnit!=null && unit.isCompatible(contextUnit)) {
unit = contextUnit;
} else {
contextUnit = convention.forcedAngularUnit;
if (contextUnit == null) {
contextUnit = angularUnit;
}
if (contextUnit!=null && unit.isCompatible(contextUnit)) {
unit = contextUnit;
}
}
}
appendSeparator(true);
final Symbols symbols = this.symbols;
final StringBuffer buffer = this.buffer;
final int start = buffer.length();
buffer.append("PARAMETER");
final int stop = buffer.length();
buffer.append(symbols.open);
setColor(Colors.Element.PARAMETER);
buffer.append(symbols.quote).append(getName(descriptor)).append(symbols.quote);
resetColor();
buffer.append(symbols.separator).append(symbols.space);
if (unit != null) {
double value;
try {
value = param.doubleValue(unit);
} catch (IllegalStateException exception) {
// May happen if a parameter is mandatory (e.g. "semi-major")
// but no value has been set for this parameter.
if (colors != null) {
final X364 c = colors.get(Colors.Element.ERROR);
if (c != null) {
buffer.insert(stop, X364.BACKGROUND_DEFAULT.sequence())
.insert(start, c.sequence());
}
}
warning = exception.getLocalizedMessage();
value = Double.NaN;
}
if (!unit.equals(valueUnit)) {
value = InternalUtilities.adjustForRoundingError(value, 360000, 9);
}
format(value);
} else {
appendObject(param.getValue());
}
buffer.append(symbols.close);
}
}
/**
* Appends the specified value to a string buffer. If the value is an array, then the
* array elements are appended recursively (i.e. the array may contains sub-array).
*/
private void appendObject(final Object value) {
final StringBuffer buffer = this.buffer;
if (value == null) {
buffer.append("null");
return;
}
final Symbols symbols = this.symbols;
if (value.getClass().isArray()) {
buffer.append(symbols.openArray);
final int length = Array.getLength(value);
for (int i=0; i unit) {
if (unit != null) {
final StringBuffer buffer = this.buffer;
final Symbols symbols = this.symbols;
appendSeparator(lineChanged);
buffer.append("UNIT").append(symbols.open);
setColor(Colors.Element.UNIT);
buffer.append(symbols.quote);
if (NonSI.DEGREE_ANGLE.equals(unit)) {
buffer.append("degree");
} else if (SI.METRE.equals(unit)) {
buffer.append(convention.unitUS ? "meter" : "metre");
} else {
unitFormat.format(unit, buffer, dummy);
}
buffer.append(symbols.quote);
resetColor();
append(Units.toStandardUnit(unit));
buffer.append(symbols.close);
}
}
/**
* Appends a character string. The string will be written between quotes.
* A comma (or any other element separator) will be written before the string if needed.
*
* @param text The string to format to the WKT.
*/
public void append(final String text) {
appendSeparator(false);
buffer.append(symbols.quote).append(text).append(symbols.quote);
}
/**
* Formats an arbitrary number.
*/
private void format(final Number number) {
if (Numbers.isInteger(number.getClass())) {
format(number.longValue());
} else {
format(number.doubleValue());
}
}
/**
* Formats an integer number.
*/
private void format(final long number) {
setColor(Colors.Element.INTEGER);
final NumberFormat numberFormat = this.numberFormat;
final int fraction = numberFormat.getMinimumFractionDigits();
numberFormat.setMinimumFractionDigits(0);
numberFormat.format(number, buffer, dummy);
numberFormat.setMinimumFractionDigits(fraction);
resetColor();
}
/**
* Formats a floating point number.
*/
private void format(double number) {
number = InternalUtilities.adjustForRoundingError(number);
setColor(Colors.Element.NUMBER);
numberFormat.format(number, buffer, dummy);
resetColor();
}
/**
* Increase or reduce the indentation. A value of {@code +1} increase
* the indentation by the amount of spaces specified at construction time,
* and a value of {@code +1} reduce it.
*/
private void indent(final int amount) {
margin = Math.max(0, margin + indentation*amount);
}
/**
* Tells if an {@code "AUTHORITY"} element is allowed for the specified object.
*/
private static boolean isAuthorityAllowed(final IdentifiedObject info) {
for (int i=0; i identifiers = info.getIdentifiers();
if (identifiers != null) {
for (final Identifier id : identifiers) {
if (Citations.identifierMatches(getAuthority(), id.getAuthority())) {
return id;
}
if (first == null) {
first = id;
}
}
}
}
return first;
}
/**
* Returns the preferred name for the specified object.
* If the specified object contains a name from the preferred authority
* (usually {@linkplain org.geotoolkit.metadata.iso.citation.Citations#OGC Open Geospatial}),
* then this name is returned. Otherwise, the first name found is returned.
*
* The preferred authority can be set by the {@link WKTFormat#setAuthority(Citation)} method.
* This is not necessarily the authority of the given {@linkplain IdentifiedObject#getName()
* object name}.
*
* Example: The EPSG name of the {@code EPSG:6326} datum is "World Geodetic System
* 1984". However if the preferred authority is OGC (which is the case by default), then
* this method usually returns "WGS84" (the exact string to be returned depends on
* the list of declared {@linkplain IdentifiedObject#getAlias() aliases}).
*
* @param info The object to looks for a preferred name.
* @return The preferred name, or {@code null} if the given object has no name.
*
* @see WKTFormat#getAuthority()
* @see IdentifiedObjects#getName(IdentifiedObject, Citation)
*/
public String getName(final IdentifiedObject info) {
String name = IdentifiedObjects.getName(info, getAuthority());
if (name == null) {
name = IdentifiedObjects.getName(info, null);
}
return name;
}
/**
* The linear unit for formatting measures, or {@code null} for the "natural" unit of each
* WKT element.
*
* @return The unit for linear measurement. Default value is {@code null}.
*/
public Unit getLinearUnit() {
return linearUnit;
}
/**
* Sets the unit for formatting linear measures.
*
* @param unit The new unit, or {@code null}.
* @throws IllegalArgumentException If the given unit is not null and not a
* {@linkplain Units#isLinear(Unit) linear unit}.
*/
public void setLinearUnit(final Unit unit) throws IllegalArgumentException {
if (unit != null && !Units.isLinear(unit)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.NON_LINEAR_UNIT_$1, unit));
}
linearUnit = unit;
}
/**
* The angular unit for formatting measures, or {@code null} for the "natural" unit of each WKT
* element. This value is set for example when parsing the "{@code GEOGCS}" element, in which
* case the enclosed "{@code PRIMEM}" element shall use the unit of the enclosing "{@code GEOGCS}".
*
* @return The unit for angle measurement. Default value is {@code null}.
*/
public Unit getAngularUnit() {
return angularUnit;
}
/**
* Sets the angular unit for formatting measures.
*
* @param unit The new unit, or {@code null}.
* @throws IllegalArgumentException If the given unit is not null and not an
* {@linkplain Units#isAngular(Unit) angular unit}.
*/
public void setAngularUnit(final Unit unit) throws IllegalArgumentException {
if (unit != null && !Units.isAngular(unit)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.NON_ANGULAR_UNIT_$1, unit));
}
angularUnit = unit;
}
/**
* Returns the authority to use for fetching the name to format.
* The internal authority is replaced by the OGC one.
*/
private Citation getAuthority() {
Citation result = authority;
if (result == null) {
Convention c = convention;
if (c == Convention.INTERNAL) {
c = Convention.OGC;
}
authority = result = c.getCitation();
}
return result;
}
/**
* Returns the convention to use for formatting the WKT. The default convention is
* {@link Convention#OGC OGC}. A different convention will usually result in different
* parameter names, but may also change the WKT syntax.
*
* @return The convention (never {@code null}).
*
* @see WKTFormat#setConvention(Convention)
* @see FormattableObject#toWKT(Convention, int)
*
* @since 3.20
*/
public Convention getConvention() {
return convention;
}
/**
* Returns {@code true} if the WKT should reflect the internal structure of the object.
* This is usually {@code true} only when debugging map projections.
*
* @return {@code true} for formatting internal WKT.
*
* @see Convention#INTERNAL
*
* @since 3.00
*
* @deprecated Replaced by {@code getConvention() == Convention.INTERNAL}.
*/
@Debug
@Deprecated
public boolean isInternalWKT() {
return convention == Convention.INTERNAL;
}
/**
* Returns {@code true} if the WKT in this formatter is not strictly compliant to the
* WKT
* specification. This method returns {@code true} if {@link #setInvalidWKT} has
* been invoked at least once. The action to take regarding invalid WKT is caller-dependent.
* For example {@link FormattableObject#toString()} will accepts loose WKT formatting and ignore
* this flag, while {@link FormattableObject#toWKT()} requires strict WKT formatting and will
* thrown an exception if this flag is set.
*
* @return {@code true} if the WKT is invalid.
*/
public boolean isInvalidWKT() {
return unformattable != null || (buffer!=null && buffer.length() == 0);
/*
* Note: we really use a "and" condition (not an other "or") for the buffer test because
* the buffer is reset to 'null' by WKTFormat after a successfull formatting.
*/
}
/**
* Returns the class declared by the last call to {@link #setInvalidWKT}.
*/
final Class> getUnformattableClass() {
return unformattable;
}
/**
* Set a flag marking the current WKT as not strictly compliant to the
* WKT
* specification. This method is invoked by {@link FormattableObject#formatWKT(Formatter)}
* methods when the object to format is more complex than what the WKT specification allows.
* For example this method is invoked when an
* {@linkplain org.geotoolkit.referencing.crs.DefaultEngineeringCRS engineering CRS} uses
* different unit for each axis, An application can tests {@link #isInvalidWKT} later for
* checking WKT validity.
*
* @param unformattable The type of the component that can't be formatted,
* for example {@link org.opengis.referencing.crs.EngineeringCRS}.
*
* @see UnformattableObjectException#getUnformattableClass
*
* @since 2.4
*/
public void setInvalidWKT(final Class> unformattable) {
this.unformattable = unformattable;
invalidWKT = true;
}
/**
* Returns the WKT in its current state.
*/
@Override
public String toString() {
return buffer.toString();
}
/**
* Clears this formatter. All properties (including {@linkplain #getLinearUnit unit}
* and {@linkplain #isInvalidWKT WKT validity flag} are reset to their default value.
* After this method call, this {@code Formatter} object is ready for formatting
* a new object.
*/
public void clear() {
if (buffer != null) {
buffer.setLength(0);
}
linearUnit = null;
angularUnit = null;
unformattable = null;
warning = null;
invalidWKT = false;
lineChanged = false;
margin = 0;
}
}