use of org.apache.sis.measure.Range in project sis by apache.
the class Parameters method getValueDomain.
/**
* Returns the domain of valid values defined by the given descriptor, or {@code null} if none.
* This method performs the following operations:
*
* <ul>
* <li>If the given parameter is an instance of {@code DefaultParameterDescriptor},
* delegate to {@link DefaultParameterDescriptor#getValueDomain()}.</li>
* <li>Otherwise builds the range from the {@linkplain DefaultParameterDescriptor#getMinimumValue() minimum value},
* {@linkplain DefaultParameterDescriptor#getMaximumValue() maximum value} and, if the values are numeric, from
* the {@linkplain DefaultParameterDescriptor#getUnit() unit}.</li>
* </ul>
*
* @param descriptor the parameter descriptor, or {@code null}.
* @return the domain of valid values, or {@code null} if none.
*
* @see DefaultParameterDescriptor#getValueDomain()
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Range<?> getValueDomain(final ParameterDescriptor<?> descriptor) {
if (descriptor != null) {
if (descriptor instanceof DefaultParameterDescriptor<?>) {
return ((DefaultParameterDescriptor<?>) descriptor).getValueDomain();
}
final Class<?> valueClass = descriptor.getValueClass();
final Comparable<?> minimumValue = descriptor.getMinimumValue();
final Comparable<?> maximumValue = descriptor.getMaximumValue();
if ((minimumValue == null || valueClass.isInstance(minimumValue)) && (maximumValue == null || valueClass.isInstance(maximumValue))) {
if (Number.class.isAssignableFrom(valueClass)) {
final Unit<?> unit = descriptor.getUnit();
if (unit != null) {
return new MeasurementRange((Class) valueClass, (Number) minimumValue, true, (Number) maximumValue, true, unit);
} else if (minimumValue != null || maximumValue != null) {
return new NumberRange((Class) valueClass, (Number) minimumValue, true, (Number) maximumValue, true);
}
} else if (minimumValue != null || maximumValue != null) {
return new Range(valueClass, minimumValue, true, maximumValue, true);
}
}
}
return null;
}
use of org.apache.sis.measure.Range 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);
}
}
}
use of org.apache.sis.measure.Range in project sis by apache.
the class Extents method getTimeRange.
/**
* Returns the union of all time ranges found in the given extent, or {@code null} if none.
*
* @param extent the extent to convert to a time range, or {@code null}.
* @return a time range created from the given extent, or {@code null} if none.
*
* @since 0.4
*/
public static Range<Date> getTimeRange(final Extent extent) {
Date min = null;
Date max = null;
if (extent != null) {
for (final TemporalExtent t : extent.getTemporalElements()) {
final Date startTime, endTime;
if (t instanceof DefaultTemporalExtent) {
final DefaultTemporalExtent dt = (DefaultTemporalExtent) t;
// Maybe user has overridden those methods.
startTime = dt.getStartTime();
endTime = dt.getEndTime();
} else {
final TemporalPrimitive p = t.getExtent();
startTime = DefaultTemporalExtent.getTime(p, true);
endTime = DefaultTemporalExtent.getTime(p, false);
}
if (startTime != null && (min == null || startTime.before(min)))
min = startTime;
if (endTime != null && (max == null || endTime.after(max)))
max = endTime;
}
}
if (min == null && max == null) {
return null;
}
return new Range<>(Date.class, min, true, max, true);
}
use of org.apache.sis.measure.Range in project sis by apache.
the class RangeSetTest method testRangeOfDates.
/**
* Tests {@link RangeSet#add(Range)} using date values.
*/
@Test
public void testRangeOfDates() {
final RangeSet<Date> ranges = RangeSet.create(Date.class, true, false);
assertTrue(ranges.isEmpty());
/*
* Add a singleton range.
*/
final Date now = new Date();
final Date yesterday = new Date(now.getTime() - MILLISECONDS_PER_DAY);
assertTrue(ranges.add(yesterday, now));
assertEquals(1, ranges.size());
checkContains(ranges, new Range<>(Date.class, yesterday, true, now, false), true, true);
/*
* Add a disjoint range.
*/
final Date lastWeek = new Date(now.getTime() - 7 * MILLISECONDS_PER_DAY);
final Date other = new Date(lastWeek.getTime() + 2 * MILLISECONDS_PER_DAY);
assertTrue(ranges.add(new Range<>(Date.class, lastWeek, true, other, false)));
assertEquals(2, ranges.size());
/*
* Verify the RangeSet content.
*/
final Iterator<Range<Date>> it = ranges.iterator();
assertEqual(new Range<>(Date.class, lastWeek, true, other, false), it.next(), ranges.first());
assertEqual(new Range<>(Date.class, yesterday, true, now, false), it.next(), ranges.last());
assertFalse(it.hasNext());
}
Aggregations