PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/geotools-9.2/modules/library/referencing/src/main/java/org/geotools/parameter/ParameterWriter.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 601 lines | 363 code | 21 blank | 217 comment | 88 complexity | d05dceaff4a60fe102aaf9c50ee586b8 MD5 | raw file
  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation;
  10. * version 2.1 of the License.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. package org.geotools.parameter;
  18. import java.io.FilterWriter;
  19. import java.io.IOException;
  20. import java.io.Writer;
  21. import java.lang.reflect.Array;
  22. import java.text.DateFormat;
  23. import java.text.NumberFormat;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.Date;
  27. import java.util.LinkedHashMap;
  28. import java.util.List;
  29. import java.util.Locale;
  30. import java.util.Map;
  31. import java.util.Set;
  32. import org.opengis.metadata.Identifier;
  33. import org.opengis.parameter.GeneralParameterDescriptor;
  34. import org.opengis.parameter.GeneralParameterValue;
  35. import org.opengis.parameter.ParameterDescriptor;
  36. import org.opengis.parameter.ParameterDescriptorGroup;
  37. import org.opengis.parameter.ParameterValue;
  38. import org.opengis.parameter.ParameterValueGroup;
  39. import org.opengis.referencing.IdentifiedObject;
  40. import org.opengis.referencing.operation.OperationMethod;
  41. import org.opengis.util.InternationalString;
  42. import org.opengis.util.GenericName;
  43. import org.geotools.io.TableWriter;
  44. import org.geotools.measure.Angle;
  45. import org.geotools.measure.AngleFormat;
  46. import org.geotools.resources.Arguments;
  47. import org.geotools.resources.Classes;
  48. import org.geotools.resources.XArray;
  49. import org.geotools.resources.i18n.Vocabulary;
  50. import org.geotools.resources.i18n.VocabularyKeys;
  51. /**
  52. * Format {@linkplain ParameterDescriptorGroup parameter descriptors} or
  53. * {@linkplain ParameterValueGroup parameter values} in a tabular format.
  54. * This writer assumes a monospaced font and an encoding capable to provide
  55. * drawing box characters (e.g. unicode).
  56. *
  57. * @since 2.1
  58. *
  59. *
  60. * @source $URL$
  61. * @version $Id$
  62. * @author Martin Desruisseaux
  63. */
  64. public class ParameterWriter extends FilterWriter {
  65. /**
  66. * The locale.
  67. */
  68. private Locale locale = Locale.getDefault();
  69. /**
  70. * The formatter to use for numbers. Will be created only when first needed.
  71. */
  72. private transient NumberFormat numberFormat;
  73. /**
  74. * The formatter to use for dates. Will be created only when first needed.
  75. */
  76. private transient DateFormat dateFormat;
  77. /**
  78. * The formatter to use for angles. Will be created only when first needed.
  79. */
  80. private transient AngleFormat angleFormat;
  81. /**
  82. * Creates a new formatter writting parameters to the
  83. * {@linkplain System#out default output stream}.
  84. */
  85. public ParameterWriter() {
  86. this(Arguments.getWriter(System.out));
  87. }
  88. /**
  89. * Creates a new formatter writting parameters to the specified output stream.
  90. */
  91. public ParameterWriter(final Writer out) {
  92. super(out);
  93. }
  94. /**
  95. * Prints the elements of an operation to the
  96. * {@linkplain System#out default output stream}.
  97. * This is a convenience method for <code>new
  98. * ParameterWriter().{@linkplain #format(OperationMethod) format}(operation)</code>.
  99. */
  100. public static void print(final OperationMethod operation) {
  101. final ParameterWriter writer = new ParameterWriter();
  102. try {
  103. writer.format(operation);
  104. } catch (IOException exception) {
  105. // Should never happen, since we are writting to System.out.
  106. throw new AssertionError(exception);
  107. }
  108. }
  109. /**
  110. * Prints the elements of a descriptor group to the
  111. * {@linkplain System#out default output stream}.
  112. * This is a convenience method for <code>new
  113. * ParameterWriter().{@linkplain #format(ParameterDescriptorGroup)
  114. * format}(descriptor)</code>.
  115. */
  116. public static void print(final ParameterDescriptorGroup descriptor) {
  117. final ParameterWriter writer = new ParameterWriter();
  118. try {
  119. writer.format(descriptor);
  120. } catch (IOException exception) {
  121. // Should never happen, since we are writting to System.out.
  122. throw new AssertionError(exception);
  123. }
  124. }
  125. /**
  126. * Prints the elements of a parameter group to the
  127. * {@linkplain System#out default output stream}.
  128. * This is a convenience method for <code>new
  129. * ParameterWriter().{@linkplain #format(ParameterValueGroup)
  130. * format}(values)</code>.
  131. */
  132. public static void print(final ParameterValueGroup values) {
  133. final ParameterWriter writer = new ParameterWriter();
  134. try {
  135. writer.format(values);
  136. } catch (IOException exception) {
  137. // Should never happen, since we are writting to System.out.
  138. throw new AssertionError(exception);
  139. }
  140. }
  141. /**
  142. * Prints the elements of an operation to the output stream.
  143. *
  144. * @param operation The operation method to format.
  145. * @throws IOException if an error occured will writing to the stream.
  146. */
  147. public void format(final OperationMethod operation) throws IOException {
  148. synchronized (lock) {
  149. format(operation.getName().getCode(), operation.getParameters(), null);
  150. }
  151. }
  152. /**
  153. * Prints the elements of a descriptor group to the output stream.
  154. *
  155. * @param descriptor The descriptor group to format.
  156. * @throws IOException if an error occured will writing to the stream.
  157. */
  158. public void format(final ParameterDescriptorGroup descriptor) throws IOException {
  159. synchronized (lock) {
  160. format(descriptor.getName().getCode(), descriptor, null);
  161. }
  162. }
  163. /**
  164. * Prints the elements of a parameter group to the output stream.
  165. *
  166. * @param values The parameter group to format.
  167. * @throws IOException if an error occured will writing to the stream.
  168. */
  169. public void format(final ParameterValueGroup values) throws IOException {
  170. final ParameterDescriptorGroup descriptor = values.getDescriptor();
  171. synchronized (lock) {
  172. format(descriptor.getName().getCode(), descriptor, values);
  173. }
  174. }
  175. /**
  176. * Implementation of public {@code format} methods.
  177. *
  178. * @param name The group name, usually {@code descriptor.getCode().getName()}.
  179. * @param descriptor The parameter descriptor. Should be equals to
  180. * {@code values.getDescriptor()} if {@code values} is non null.
  181. * @param values The parameter values, or {@code null} if none.
  182. * @throws IOException if an error occured will writing to the stream.
  183. */
  184. private void format(final String name,
  185. final ParameterDescriptorGroup group,
  186. final ParameterValueGroup values)
  187. throws IOException
  188. {
  189. /*
  190. * Write the operation name (including aliases) before the table.
  191. */
  192. final String lineSeparator = System.getProperty("line.separator", "\n");
  193. out.write(' ');
  194. out.write(name);
  195. out.write(lineSeparator);
  196. Collection<GenericName> alias = group.getAlias();
  197. if (alias != null) {
  198. boolean first = true;
  199. for (final GenericName a : alias) {
  200. out.write(first ? " alias " : " ");
  201. out.write(a.toInternationalString().toString(locale));
  202. out.write(lineSeparator);
  203. first = false;
  204. }
  205. }
  206. /*
  207. * Format the table header (i.e. column names).
  208. */
  209. final Vocabulary resources = Vocabulary.getResources(locale);
  210. final TableWriter table = new TableWriter(out, TableWriter.SINGLE_VERTICAL_LINE);
  211. table.setMultiLinesCells(true);
  212. table.writeHorizontalSeparator();
  213. table.write(resources.getString(VocabularyKeys.NAME));
  214. table.nextColumn();
  215. table.write(resources.getString(VocabularyKeys.CLASS));
  216. table.nextColumn();
  217. table.write("Minimum"); // TODO localize
  218. table.nextColumn();
  219. table.write("Maximum"); // TODO localize
  220. table.nextColumn();
  221. table.write(resources.getString((values==null) ? VocabularyKeys.DEFAULT_VALUE
  222. : VocabularyKeys.VALUE));
  223. table.nextColumn();
  224. table.write("Units"); // TODO localize
  225. table.nextLine();
  226. table.nextLine(TableWriter.DOUBLE_HORIZONTAL_LINE);
  227. /*
  228. * Format each element in the parameter group. If values were supplied, we will
  229. * iterate through the values instead of the descriptor. We do it that way because
  230. * the descriptor can't know which optional values are included and which one are
  231. * omitted.
  232. */
  233. List<Object> deferredGroups = null;
  234. final Object[] array1 = new Object[1];
  235. final Collection<?> elements = (values!=null) ? values.values() : group.descriptors();
  236. for (final Object element : elements) {
  237. final GeneralParameterValue generalValue;
  238. final GeneralParameterDescriptor generalDescriptor;
  239. if (values != null) {
  240. generalValue = (GeneralParameterValue) element;
  241. generalDescriptor = generalValue.getDescriptor();
  242. } else {
  243. generalValue = null;
  244. generalDescriptor = (GeneralParameterDescriptor) element;
  245. }
  246. /*
  247. * If the current element is a group, we will format it later (after
  248. * all ordinary elements) in order avoid breaking the table layout.
  249. */
  250. if (generalDescriptor instanceof ParameterDescriptorGroup) {
  251. if (deferredGroups == null) {
  252. deferredGroups = new ArrayList<Object>();
  253. }
  254. deferredGroups.add(element);
  255. continue;
  256. }
  257. /*
  258. * Format the element name, including all alias (if any).
  259. * Each alias will be formatted on its own line.
  260. */
  261. final Identifier identifier = generalDescriptor.getName();
  262. table.write(identifier.getCode());
  263. alias = generalDescriptor.getAlias();
  264. if (alias != null) {
  265. for (final GenericName a : alias) {
  266. if (!identifier.equals(a)) {
  267. table.write(lineSeparator);
  268. table.write(a.tip().toInternationalString().toString(locale));
  269. }
  270. }
  271. }
  272. table.nextColumn();
  273. /*
  274. * Format the current element as an ordinary descriptor. If we are iterating
  275. * over the descriptors rather than values, then the "value" column will be
  276. * filled with the default value specified in descriptors.
  277. */
  278. if (generalDescriptor instanceof ParameterDescriptor) {
  279. final ParameterDescriptor descriptor = (ParameterDescriptor) generalDescriptor;
  280. table.write(Classes.getShortName(descriptor.getValueClass()));
  281. table.nextColumn();
  282. table.setAlignment(TableWriter.ALIGN_RIGHT);
  283. Object value = descriptor.getMinimumValue();
  284. if (value != null) {
  285. table.write(formatValue(value));
  286. }
  287. table.nextColumn();
  288. value = descriptor.getMaximumValue();
  289. if (value != null) {
  290. table.write(formatValue(value));
  291. }
  292. table.nextColumn();
  293. if (generalValue != null) {
  294. value = ((ParameterValue) generalValue).getValue();
  295. } else {
  296. value = descriptor.getDefaultValue();
  297. }
  298. /*
  299. * Wraps the value in an array. Because it may be an array of primitive
  300. * type, we can't cast to Object[]. Then, each array's element will be
  301. * formatted on its own line.
  302. */
  303. final Object array;
  304. if (value!=null && value.getClass().isArray()) {
  305. array = value;
  306. } else {
  307. array = array1;
  308. array1[0] = value;
  309. }
  310. final int length = Array.getLength(array);
  311. for (int i=0; i<length; i++) {
  312. value = Array.get(array, i);
  313. if (value != null) {
  314. if (i != 0) {
  315. table.write(lineSeparator);
  316. }
  317. table.write(formatValue(value));
  318. }
  319. }
  320. table.nextColumn();
  321. table.setAlignment(TableWriter.ALIGN_LEFT);
  322. value = descriptor.getUnit();
  323. if (value != null) {
  324. table.write(value.toString());
  325. }
  326. }
  327. table.writeHorizontalSeparator();
  328. }
  329. table.flush();
  330. /*
  331. * Now format all groups deferred to the end of this table.
  332. * Most of the time, there is no such group.
  333. */
  334. if (deferredGroups != null) {
  335. for (final Object element : deferredGroups) {
  336. final ParameterValueGroup value;
  337. final ParameterDescriptorGroup descriptor;
  338. if (element instanceof ParameterValueGroup) {
  339. value = (ParameterValueGroup) element;
  340. descriptor = value.getDescriptor();
  341. } else {
  342. value = null;
  343. descriptor = (ParameterDescriptorGroup) element;
  344. }
  345. out.write(lineSeparator);
  346. format(name + '/' + descriptor.getName().getCode(), descriptor, value);
  347. }
  348. }
  349. }
  350. /**
  351. * Formats a summary of a collection of {@linkplain IdentifiedObject identified objects}.
  352. * The summary contains the identifier name and alias aligned in a table.
  353. *
  354. * @param parameters The collection of parameters to format.
  355. * @param scopes The set of scopes to include in the table, of {@code null} for all
  356. * of them. A restricted a set will produce a table with less columns.
  357. * @throws IOException if an error occured will writing to the stream.
  358. */
  359. public void summary(final Collection<? extends IdentifiedObject> parameters,
  360. final Set<String> scopes) throws IOException
  361. {
  362. /*
  363. * Prepares the list of alias before any write to the output stream.
  364. * We need to prepare the list first, because not all identified objects
  365. * may have generic names with the same scopes in the same order.
  366. *
  367. * titles - The column number for each column title.
  368. * names - The names (including alias) for each line.
  369. */
  370. final Map<Object,Integer> titles = new LinkedHashMap<Object,Integer>();
  371. final List<String[]> names = new ArrayList<String[]>();
  372. final Locale locale = this.locale; // Protect from changes.
  373. String[] descriptions = null;
  374. titles.put(null, 0); // Special value for the identifier column.
  375. for (final IdentifiedObject element : parameters) {
  376. final Collection<GenericName> aliases = element.getAlias();
  377. String[] elementNames = new String[titles.size()];
  378. elementNames[0] = element.getName().getCode();
  379. if (aliases != null) {
  380. /*
  381. * The primary name has been fetch (before this block) for one element, and we
  382. * determined that some alias may be available in addition. Add local alias
  383. * (i.e. names without their scope) to the 'elementNames' row.
  384. */
  385. int count = 0;
  386. for (final GenericName alias : aliases) {
  387. final GenericName scope = alias.scope().name();
  388. final GenericName name = alias.tip();
  389. final Object title;
  390. if (scope != null) {
  391. if (scopes!=null && !scopes.contains(scope.toString())) {
  392. /*
  393. * The user requested only a subset of alias (the 'scopes' argument),
  394. * and the current alias is not a member of this subset. Continue the
  395. * search to other alias.
  396. */
  397. continue;
  398. }
  399. title = scope.toInternationalString().toString(locale);
  400. } else {
  401. title = count++;
  402. }
  403. /*
  404. * The alias scope is used as the column's title. If the alias has no scope,
  405. * then a sequencial number is used instead. Now check if the column already
  406. * exists. If it exists, fetch its position. If it doesn't exist, inserts the
  407. * new column at the end of existing columns.
  408. */
  409. Integer position = titles.get(title);
  410. if (position == null) {
  411. position = titles.size();
  412. titles.put(title, position);
  413. }
  414. /*
  415. * Now stores the alias local name at the position we just determined above.
  416. * Note that more than one value may exist for the same column. For example
  417. * both "WGS 84" and "4326" may appear as EPSG alias (as EPSG name and EPSG
  418. * identifier respectively), depending how the parameters given by the user
  419. * were constructed.
  420. */
  421. final int index = position.intValue();
  422. if (index >= elementNames.length) {
  423. elementNames = XArray.resize(elementNames, index+1);
  424. }
  425. final String oldName = elementNames[index];
  426. final String newName = name.toInternationalString().toString(locale);
  427. if (oldName==null || oldName.length()>newName.length()) {
  428. /*
  429. * Keep the shortest string, since it is often a code used
  430. * for identification (e.g. EPSG code). It also help to fit
  431. * the table in the window's width.
  432. */
  433. elementNames[index] = newName;
  434. }
  435. }
  436. }
  437. /*
  438. * Before to add the name and alias to the list, fetch the remarks (if any).
  439. * They are stored in a separated list and will appear as the very last column.
  440. */
  441. final InternationalString remarks = element.getRemarks();
  442. if (remarks != null) {
  443. if (descriptions == null) {
  444. descriptions = new String[parameters.size()];
  445. }
  446. descriptions[names.size()] = remarks.toString(locale);
  447. }
  448. names.add(elementNames);
  449. }
  450. /*
  451. * Trim the columns that duplicates the identifier column (#0). This is
  452. * usually the case of the OGC column (usually #1), since we already use
  453. * OGC name as the main identifier in most cases.
  454. */
  455. final boolean[] hide = new boolean[titles.size()];
  456. trim: for (int column=hide.length; --column>=1;) {
  457. for (final String[] alias : names) {
  458. if (alias.length > column) {
  459. final String name = alias[column];
  460. if (name!=null && !name.equals(alias[0])) {
  461. // No need to looks at the next lines.
  462. // Move to previous column.
  463. continue trim;
  464. }
  465. }
  466. }
  467. // A column duplicating the identifier column has been found.
  468. hide[column] = true;
  469. }
  470. /*
  471. * Writes the table. The header will contains one column for each alias's
  472. * scope (or authority) declared in 'titles', in the same order. It will
  473. * also contains a "Description" column if there is some.
  474. */
  475. int column = 0;
  476. synchronized (lock) {
  477. final TableWriter table = new TableWriter(out, TableWriter.SINGLE_VERTICAL_LINE);
  478. table.setMultiLinesCells(true);
  479. table.writeHorizontalSeparator();
  480. /*
  481. * Writes all column headers.
  482. */
  483. for (final Object element : titles.keySet()) {
  484. if (hide[column++]) {
  485. continue;
  486. }
  487. final String title;
  488. if (element == null) {
  489. title = "Identifier"; // TODO: localize
  490. } else if (element instanceof String) {
  491. title = (String) element;
  492. } else {
  493. title = "Alias " + element; // TODO: localize
  494. }
  495. table.write(title);
  496. table.nextColumn();
  497. }
  498. if (descriptions != null) {
  499. table.write("Description"); // TODO: localize
  500. }
  501. table.writeHorizontalSeparator();
  502. /*
  503. * Writes all row.
  504. */
  505. int counter = 0;
  506. for (final String[] aliases : names) {
  507. for (column=0; column<hide.length; column++) {
  508. if (hide[column]) {
  509. continue;
  510. }
  511. if (column < aliases.length) {
  512. final String alias = aliases[column];
  513. if (alias != null) {
  514. table.write(alias);
  515. }
  516. }
  517. table.nextColumn();
  518. }
  519. if (descriptions != null) {
  520. final String remarks = descriptions[counter++];
  521. if (remarks != null) {
  522. table.write(remarks);
  523. }
  524. }
  525. table.nextLine();
  526. }
  527. table.writeHorizontalSeparator();
  528. table.flush();
  529. }
  530. }
  531. /**
  532. * Returns the current locale. Newly constructed {@code ParameterWriter}
  533. * use the {@linkplain Locale#getDefault system default}.
  534. */
  535. public Locale getLocale() {
  536. return locale;
  537. }
  538. /**
  539. * Set the locale to use for table formatting.
  540. */
  541. public void setLocale(final Locale locale) {
  542. synchronized (lock) {
  543. this.locale = locale;
  544. numberFormat = null;
  545. dateFormat = null;
  546. angleFormat = null;
  547. }
  548. }
  549. /**
  550. * Format the specified value as a string. This method is automatically invoked
  551. * by {@code format(...)} methods. The default implementation format
  552. * {@link Number}, {@link Date} and {@link Angle} object according the
  553. * {@linkplain #getLocale current locale}. This method can been overridden if
  554. * more objects need to be formatted in a special way.
  555. *
  556. * @param value the value to format.
  557. * @return The value formatted as a string.
  558. */
  559. protected String formatValue(final Object value) {
  560. if (value instanceof Number) {
  561. if (numberFormat == null) {
  562. numberFormat = NumberFormat.getNumberInstance(locale);
  563. }
  564. return numberFormat.format(value);
  565. }
  566. if (value instanceof Date) {
  567. if (dateFormat == null) {
  568. dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
  569. }
  570. return dateFormat.format(value);
  571. }
  572. if (value instanceof Angle) {
  573. if (angleFormat == null) {
  574. angleFormat = AngleFormat.getInstance(locale);
  575. }
  576. return angleFormat.format(value);
  577. }
  578. return String.valueOf(value);
  579. }
  580. }