sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1672784 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/referencing/operation/projection/ main/java/org/apache/sis/referencing/operation/transform/ test/java/org/apache/sis/internal/referencing/provider/ test/jav...
Date Fri, 10 Apr 2015 22:59:41 GMT
Author: desruisseaux
Date: Fri Apr 10 22:59:40 2015
New Revision: 1672784

URL: http://svn.apache.org/r1672784
Log:
New HTML generator for the page listing the map projection parameters.

Added:
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java
      - copied, changed from r1672514, sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProjectionParametersReport.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java
  (with props)
Removed:
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProjectionParametersReport.java
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java?rev=1672784&r1=1672783&r2=1672784&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
[UTF-8] Fri Apr 10 22:59:40 2015
@@ -36,7 +36,7 @@
  * More on this convention is explained below.</p>
  *
  * <p>Users wanting to know more about the available projections and their parameters
should look at the
- * <a href="http://sis.apache.org/CoordinateOperationMethods.html">list of coordinate
operation methods</a>.
+ * <a href="http://sis.apache.org/content/CoordinateOperationMethods.html">list of
coordinate operation methods</a>.
  * Only users interested in the <em>implementation</em> of those projections
should look at this package.</p>
  *
  *

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1672784&r1=1672783&r2=1672784&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
[UTF-8] Fri Apr 10 22:59:40 2015
@@ -80,7 +80,7 @@ import org.apache.sis.util.resources.Mes
  * <div class="section">Standard parameters</div>
  * {@code MathTransform} instances are created from {@linkplain org.apache.sis.parameter.DefaultParameterValueGroup
  * parameter values}. The parameters expected by each operation available in a default Apache
SIS installation is
- * <a href="http://sis.apache.org/CoordinateOperationMethods.html">listed here</a>.
+ * <a href="http://sis.apache.org/content/CoordinateOperationMethods.html">listed here</a>.
  * The set of parameters varies for each operation or projection, but the following can be
considered typical:
  *
  * <ul>

Copied: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java
(from r1672514, sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProjectionParametersReport.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java?p2=sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java&p1=sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProjectionParametersReport.java&r1=1672514&r2=1672784&rev=1672784&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProjectionParametersReport.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java
[UTF-8] Fri Apr 10 22:59:40 2015
@@ -17,45 +17,56 @@
 package org.apache.sis.internal.referencing.provider;
 
 import java.util.Map;
-import java.util.Set;
-import java.util.LinkedHashSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.io.File;
 import java.io.IOException;
+import javax.measure.unit.Unit;
+import org.opengis.util.FactoryException;
 import org.opengis.util.GenericName;
-import org.opengis.metadata.citation.Citation;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.referencing.IdentifiedObject;
-import org.opengis.referencing.operation.Projection;
-import org.opengis.referencing.operation.Conversion;
-import org.opengis.referencing.operation.Transformation;
-import org.opengis.referencing.operation.SingleOperation;
-import org.opengis.referencing.operation.MathTransformFactory;
-import org.opengis.test.report.OperationParametersReport;
-import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.opengis.metadata.Identifier;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.parameter.*;
+import org.opengis.referencing.operation.*;
+import org.opengis.referencing.crs.CRSAuthorityFactory;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.GeneralDerivedCRS;
 import org.apache.sis.internal.system.DefaultFactories;
-import org.apache.sis.metadata.iso.ImmutableIdentifier;
-import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.internal.util.Constants;
-import org.apache.sis.util.Version;
-
-import static org.apache.sis.util.Characters.NO_BREAK_SPACE;
-import static org.apache.sis.util.collection.Containers.hashMapCapacity;
+import org.apache.sis.measure.Range;
+import org.apache.sis.measure.Latitude;
+import org.apache.sis.measure.Longitude;
+import org.apache.sis.measure.RangeFormat;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.Characters;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.test.HTMLGenerator;
 
 
 /**
  * Generates a list of projection parameters in a HTML page. This class is used for updating
the
- * <a href="http://sis.apache.org/CoordinateOperationMethods.html">CoordinateOperationMethods.html</a>
page.
- * The {@linkplain #main(String[])} method creates the "{@code CoordinateOperationMethods.html}"
file in the
- * current default directory if it does not already exists. Users is responsible for moving
the generated
- * file to the Apache SIS site directory.
+ * <a href="http://sis.apache.org/content/CoordinateOperationMethods.html">CoordinateOperationMethods.html</a>
page.
+ * The {@linkplain #main(String[])} method creates the "{@code CoordinateOperationMethods.html}"
file in the current
+ * default directory if it does not already exists. Users is responsible for moving the generated
file to the Apache
+ * SIS {@code "content/"} site directory.
+ *
+ * <p><b>This class is designed for Apache SIS operation methods only</b>
- this is not a general purpose generator
+ * for arbitrary operation methods. The reason is that we make some assumptions in various
place (e.g. EPSG name is
+ * first, no HTML characters to escape in non-EPSG identifiers, etc.).</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
  * @version 0.6
  * @module
  */
-public final class ProjectionParametersReport extends OperationParametersReport {
+public final class CoordinateOperationMethodsHTML extends HTMLGenerator {
     /**
      * Generates the HTML report.
      *
@@ -63,172 +74,462 @@ public final class ProjectionParametersR
      * @throws IOException If an error occurred while writing the HTML file.
      */
     public static void main(final String[] args) throws IOException {
-        if (args.length != 0) {
-            System.err.println("This command does not expect any argument.");
-            return;
-        }
-        final File file = new File("operation-parameters.html");
-        if (file.exists()) {
-            System.err.println("File " + file + " already exists.");
-            return;
+        final MathTransformFactory factory = DefaultFactories.forBuildin(MathTransformFactory.class);
+        final List<OperationMethod> methods = new ArrayList<>(factory.getAvailableMethods(SingleOperation.class));
+        Collections.sort(methods, (final OperationMethod o1, final OperationMethod o2) ->
{
+            int c = category(o1) - category(o2);
+            if (c == 0) {  // If the two methods are in the same category, sort by name.
+                c = o1.getName().getCode().compareTo(o2.getName().getCode());
+            }
+            return c;
+        });
+        try (final CoordinateOperationMethodsHTML writer = new CoordinateOperationMethodsHTML())
{
+            writer.writeIndex(methods);
+            for (final OperationMethod method : methods) {
+                writer.write(method);
+            }
         }
-        System.out.println("Write " + file.getAbsolutePath());
-        /*
-         * Note: ESRI needs to be right after OGC in the above list because the createRow(…)
method in this class
-         * contains an empirical hack for allowing the GeoAPI report to merge long ESRI projection
names with the
-         * OGC name when the two names are identical (ignoring case).
-         */
-        final ProjectionParametersReport writer = new ProjectionParametersReport(
-                Citations.EPSG,   Citations.OGC,     Citations.ESRI,
-                Citations.NETCDF, Citations.GEOTIFF, Citations.PROJ4);
-        writer.add(DefaultFactories.forClass(MathTransformFactory.class));
-        writer.write(file);
     }
 
     /**
-     * All authority names as {@link String} instances. Those names will be used as column
headers
-     * in the table of coordinate operation methods. Those headers will typically be "EPSG",
"OGC",
-     * "ESRI", "NetCDF", "GeoTIFF" and "PROJ4".
+     * Values returned by {@link #category(OperationMethod)}.
      */
-    private final Set<String> columnHeaders;
+    private static final int PROJECTION = 1, CONVERSION = 2, TRANSFORMATION = 3;
 
     /**
-     * The type of coordinate operation methods, in the order to be shown in the HTML report.
-     * We will typically show map projections first, followed by coordinate conversions,
-     * followed by coordinate transformations.
+     * Parameters to default to the latitude of origin. We can hardly detect those cases
+     * automatically, since the behavior for the default value is hard-coded in Java.
+     *
+     * @todo Not yet completed.
      */
-    private final Class<? extends SingleOperation>[] categories;
+    private final GeneralParameterDescriptor defaultToLatitudeOfOrigin[] = {
+//      AlbersEqualArea            .PARAMETERS.descriptor("Latitude of 1st standard parallel"),
+//      LambertConformal2SP        .PARAMETERS.descriptor("Latitude of 1st standard parallel"),
+//      LambertConformal2SP.Belgium.PARAMETERS.descriptor("Latitude of 1st standard parallel")
+    };
 
     /**
-     * Creates a new instance which will use the parameter names and aliases of the given
authorities.
+     * Parameters to default to the first standard parallel. We can hardly detect those
+     * cases automatically, since the behavior for the default value is hard-coded in Java.
      *
-     * @param authorities The authorities for which to show parameter names and aliases.
+     * @todo Not yet completed.
      */
-    @SuppressWarnings({"unchecked","rawtypes"})
-    private ProjectionParametersReport(final Citation... authorities) {
-        super(null);
-        /*
-         * For a list of legal property names, see:
-         * http://www.geoapi.org/geoapi-conformance/apidocs/org/opengis/test/report/OperationParametersReport.html
-         */
-        properties.setProperty("TITLE",           "Coordinate Operation parameters");
-        properties.setProperty("PRODUCT.NAME",    "Apache SIS");
-        properties.setProperty("PRODUCT.VERSION",  Version.SIS.toString());
-        properties.setProperty("PRODUCT.URL",     "http://sis.apache.org");
-        final Set<String> columns = new LinkedHashSet<>(hashMapCapacity(authorities.length));
-        for (final Citation authority : authorities) {
-            columns.add(org.apache.sis.internal.util.Citations.getCodeSpace(authority));
-        }
-        columnHeaders = Collections.unmodifiableSet(columns);
-        categories = new Class[] {
-            Projection.class,
-            Conversion.class,
-            Transformation.class
-        };
+    private final GeneralParameterDescriptor defaultToStandardParallel1[] = {
+//      AlbersEqualArea            .PARAMETERS.descriptor("Latitude of 2nd standard parallel"),
+//      LambertConformal2SP        .PARAMETERS.descriptor("Latitude of 2nd standard parallel"),
+//      LambertConformal2SP.Belgium.PARAMETERS.descriptor("Latitude of 2nd standard parallel")
+    };
+
+    /**
+     * Parameters to default to the azimuth. We can hardly detect those cases automatically,
+     * since the behavior for the default value is hard-coded in Java.
+     *
+     * @todo Not yet completed.
+     */
+    private final GeneralParameterDescriptor defaultToAzimuth[] = {
+//      ObliqueMercator      .PARAMETERS.descriptor("Angle from Rectified to Skew Grid"),
+//      HotineObliqueMercator.PARAMETERS.descriptor("Angle from Rectified to Skew Grid")
+    };
+
+    /**
+     * The union of domain of validity of all map projections using a method of the given
name.
+     * Keys are {@link OperationMethod} names, and values are the union of the domain of
validity
+     * of all CRS using that {@code OperationMethod}.
+     *
+     * @see #computeUnionOfAllDomainOfValidity(CRSAuthorityFactory)
+     */
+    private final Map<String, DefaultGeographicBoundingBox> domainOfValidity;
+
+    /**
+     * The object to use for formatting ranges.
+     */
+    private final RangeFormat rangeFormat;
+
+    /**
+     * Creates a new HTML generator for parameters.
+     *
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    public CoordinateOperationMethodsHTML() throws IOException {
+        super("CoordinateOperationMethods.html", "Apache SIS™ Coordinate Operation Methods");
+        domainOfValidity = Collections.emptyMap(); // TODO: not yet available.
+        rangeFormat = new RangeFormat(LOCALE);
+        println("p", "The following tables summarize the coordinate operation methods known
to Apache SIS, "
+                   + "together with the recognized parameters.");
     }
 
     /**
-     * Creates a new row for the given operation and parameters. The given code spaces will
be ignored;
-     * we will use our own code spaces derived from the citations given at construction time
instead.
-     *
-     * @param  operation  The operation.
-     * @param  parameters The operation parameters, or {@code null} if none.
-     * @param  codeSpaces The code spaces for which to get the name and aliases.
-     * @return The new row, or {@code null} if none.
-     */
-    @Override
-    protected Row createRow(final IdentifiedObject operation, final ParameterDescriptorGroup
parameters, final Set<String> codeSpaces) {
-        final Row row = super.createRow(operation, parameters, columnHeaders);
+     * Writes a table of content.
+     *
+     * @param  methods The methods to write to the HTML file.
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    public void writeIndex(final Iterable<? extends OperationMethod> methods) throws
IOException {
+        printlnHTML("p", "<b>Table of content:</b>");
+        open("ul");
+        int category = 0;
+        for (final OperationMethod method : methods) {
+            final int nc = category(method);
+            if (nc != category) {
+                if (category != 0) {
+                    close("ul");
+                    close("li");
+                }
+                open("li");
+                switch (nc) {
+                    case PROJECTION:     println("Projections");    break;
+                    case CONVERSION:     println("Conversions");    break;
+                    case TRANSFORMATION: println("Tranformations"); break;
+                    default: throw new AssertionError(category);
+                }
+                open("ul");
+                category = nc;
+            }
+            printlnHTML("li", "<a href=\"#" + getAnchor(method) + "\">" + escape(method.getName().getCode())
+ "</a>");
+        }
+        if (category != 0) {
+            close("ul");
+            close("li");
+        }
+        close("ul");
+    }
+
+    /**
+     * Writes identification info and parameters for the given method.
+     *
+     * @param  method The method to write to the HTML file.
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    public void write(final OperationMethod method) throws IOException {
+        println("h2 id=\"" + getAnchor(method) + '"', method.getName().getCode());
+        open("blockquote");
+        writeIdentification(method);
+        writeParameters(method.getParameters());
+        close("blockquote");
+    }
+
+    /**
+     * Writes identification info about the given method.
+     * This method writes the following information:
+     *
+     * <ul>
+     *   <li>EPSG codes</li>
+     *   <li>Aliases</li>
+     *   <li>Domain of validity</li>
+     * </ul>
+     */
+    private void writeIdentification(final OperationMethod method) throws IOException {
+        open("table class=\"info\"");
         /*
-         * Find a user category for the given object. If a category is found, it will be
formatted as a single row in
-         * the HTML table before all subsequent objects of the same category. Note that in
order to get good results,
-         * the Row.compare(...) method needs to be defined in such a way that objects of
the same category are grouped
-         * together.
+         * ────────────────    EPSG IDENTIFIERS    ────────────────────────────────────
          */
-        int categoryIndex = categories.length;
-        if (operation instanceof DefaultOperationMethod) {
-            final Class<? extends SingleOperation> c = ((DefaultOperationMethod) operation).getOperationType();
-            if (c != null) {
-                for (int i=0; i<categoryIndex; i++) {
-                    final Class<?> category = categories[i];
-                    if (category.isAssignableFrom(c)) {
-                        if (category == Projection.class) {
-                            row.category = "Map projections";
-                        } else {
-                            row.category = category.getSimpleName() + 's';
-                        }
-                        categoryIndex = i;
-                        break;
-                    }
+        final StringBuilder buffer = new StringBuilder();
+        for (final Identifier id : method.getIdentifiers()) {
+            if (Constants.EPSG.equalsIgnoreCase(id.getCodeSpace())) {
+                if (buffer.length() != 0) {
+                    buffer.append(", ");
+                }
+                final boolean isDeprecated = isDeprecated(id);
+                if (isDeprecated) {
+                    buffer.append("<del>");
+                }
+                buffer.append(id.getCode());
+                if (isDeprecated) {
+                    buffer.append("</del>");
                 }
             }
         }
+        if (buffer.length() != 0) {
+            open("tr");
+            println("th", "EPSG code:");
+            printlnHTML("td", buffer);
+            close("tr");
+        }
         /*
-         * Empirical adjustment in the table layout:  for a few very long ESRI names, just
declare
-         * that the name is the same than the OGC name. This allow the GeoAPI report to generate
a
-         * more compact HTML table, by avoiding the column space required when repeating
the same
-         * information twice.
+         * ────────────────    ALIASES    ─────────────────────────────────────────────
          */
-        String names[] = row.names.get("ESRI");
-        if (names != null && names.length == 1) {
-            final String name = names[0];
-            switch (name) {
-                case "Lambert_Azimuthal_Equal_Area":
-                case "Lambert_Conformal_Conic_2SP_Belgium": {
-                    names = row.names.get(Constants.OGC);
-                    assert names.length == 1 && names[0].contains(name) : name;
-                    names[0] += " " + NO_BREAK_SPACE + "<font size=\"-1\" color=\"MediumSlateBlue\">(ESRI:
same name)</font>";
-                    row.names.remove("ESRI");
-                    break;
-                }
+        buffer.setLength(0);
+        for (final GenericName alias : method.getAlias()) {
+            if (buffer.length() != 0) {
+                buffer.append(", ");
+            }
+            final GenericName head = alias.head();
+            if (head == alias || Constants.EPSG.equalsIgnoreCase(head.toString())) {
+                buffer.append(alias.tip());
+            } else {
+                buffer.append("<span class=\"non-epsg\">").append(head).append(":</span>")
+                      .append("<code>").append(alias.tip()).append("</code>");
             }
         }
+        if (buffer.length() != 0) {
+            open("tr");
+            println("th", "Aliases:");
+            printlnHTML("td", buffer);
+            close("tr");
+        }
         /*
-         * Search for deprecated names. We will render them as deleted name.
+         * ────────────────    DOMAIN OF VALIDITY    ──────────────────────────────────
          */
-        for (final Map.Entry<String,String[]> entry : row.names.entrySet()) {
-            final String authority = entry.getKey();
-            for (final GenericName candidate : operation.getAlias()) {
-                if (candidate instanceof ImmutableIdentifier) {
-                    final ImmutableIdentifier identifier = (ImmutableIdentifier) candidate;
-                    if (identifier.isDeprecated() && authority.equalsIgnoreCase(identifier.getCodeSpace()))
{
-                        final String[] codes = entry.getValue();
-                        final String deprecated = identifier.getCode();
-                        for (int i=0; i<codes.length; i++) {
-                            final String code = codes[i];
-                            if (code.equalsIgnoreCase(deprecated)) {
-                                codes[i] = "<del>" + code + "</del>";
-                                break; // Continue the outer loop.
-                            }
-                        }
+        buffer.setLength(0);
+        final DefaultGeographicBoundingBox domain = getDomainOfValidity(method);
+        if (domain != null) {
+            open("tr");
+            println("th", "Domain of validity:");
+            println("td", buffer.append(new Latitude (domain.getSouthBoundLatitude())).append("
to ")
+                                .append(new Latitude (domain.getNorthBoundLatitude())).append("
and ")
+                                .append(new Longitude(domain.getWestBoundLongitude())).append("
to ")
+                                .append(new Longitude(domain.getEastBoundLongitude())));
+            close("tr");
+        }
+        close("table");
+    }
+
+    /**
+     * Writes the table of parameters.
+     * Table columns will be:
+     *
+     * <ul>
+     *   <li>First EPSG code</li>
+     *   <li>Primary name</li>
+     *   <li>Reference to remarks, if any</li>
+     *   <li>Domain of values</li>
+     *   <li>Default values</li>
+     * </ul>
+     */
+    private void writeParameters(final ParameterDescriptorGroup group) throws IOException
{
+        open   ("table class=\"param\"");
+        println("caption", "Operation parameters:");
+        open   ("tr");
+        println("th", "EPSG");
+        println("th class=\"sep\"", "Name");
+        println("th class=\"sep\"", "Remarks");
+        println("th class=\"sep\" colspan=\"3\"", "Value domain");
+        println("th class=\"sep\"", "Default");
+        final Map<String, Integer> footnotes = new LinkedHashMap<>();
+        for (final GeneralParameterDescriptor gp : group.descriptors()) {
+            final ParameterDescriptor<?> param = (ParameterDescriptor<?>) gp;
+            reopen ("tr", "tr");
+            println("td", getFirstEpsgCode(param.getIdentifiers()));
+            writeName(param);
+            String remarks = toLocalizedString(param.getRemarks());
+            if (remarks != null) {
+                Integer index = footnotes.putIfAbsent(remarks, footnotes.size() + 1);
+                if (index == null) {
+                    index = footnotes.size();
+                }
+                remarks = ((param.getMinimumOccurs() != 0) ? "Unmodifiable " : "Optional
") + toSuperScript(index);
+            }
+            println("td class=\"sep\"", remarks);
+            final String domain = toLocalizedString(Parameters.getValueDomain(param));
+            final int s;
+            if (domain != null && ((s = domain.indexOf('…')) >= 0)) {
+                println("td class=\"sep right\"", domain.substring(0, s).trim());
+                println("td class=\"center\"", "…");
+                println("td class=\"left\"", domain.substring(s + 1).trim());
+            } else {
+                println("td class=\"sep center\" colspan=\"3\"", domain);
+            }
+            println("td class=\"sep\"", getDefaultValue(param, getUnit(param)));
+        }
+        close("tr");
+        close("table");
+        if (!footnotes.isEmpty()) {
+            open("table class=\"footnotes\"");
+            for (final Map.Entry<String,Integer> entry : footnotes.entrySet()) {
+                open("tr");
+                println("td", String.valueOf(toSuperScript(entry.getValue())));
+                println("td", entry.getKey());
+                close("tr");
+            }
+            close("table");
+        }
+    }
+
+    /**
+     * Writes the primary name and aliases.
+     */
+    private void writeName(final ParameterDescriptor<?> param) throws IOException {
+        open("td class=\"sep\"");
+        open("details");
+        final Identifier name = param.getName();
+        final String codeSpace = name.getCodeSpace();
+        if (Constants.EPSG.equalsIgnoreCase(codeSpace)) {
+            println("summary", name.getCode());
+        } else {
+            printlnHTML("summary", "<span class=\"non-epsg\">" + codeSpace
+                    + ":</span><code>" + name.getCode() + "</code>");
+        }
+        open("table class=\"aliases\"");
+        for (final GenericName alias : param.getAlias()) {
+            open("tr");
+            println("th", alias.head().toString() + ':');
+            println("td", alias.tip().toString());
+            close("tr");
+        }
+        close("table");
+        close("details");
+        close("td");
+    }
+
+    /**
+     * For each {@link OperationMethod} (identified by their name), computes the union of
the domain of validity
+     * of all CRS using that operation method. The result is a map where keys are {@link
OperationMethod} names,
+     * and values are the union of the domain of validity of all CRS using that {@code OperationMethod}.
+     *
+     * <p>This is a costly operation.</p>
+     *
+     * @todo This method is not yet used. This is pending the implementation of {@code CRSAuthorityFactory}
is SIS.
+     *
+     * @param  factory The factory to use for getting CRS.
+     * @return The union of domain of validity of all map projections using a method of the
given name.
+     * @throws FactoryException If an error occurred while fetching the list of CRS.
+     */
+    public static Map<String, DefaultGeographicBoundingBox> computeUnionOfAllDomainOfValidity(
+            final CRSAuthorityFactory factory) throws FactoryException
+    {
+        final Map<String, DefaultGeographicBoundingBox> domainOfValidity = new HashMap<>();
+        for (final String code : factory.getAuthorityCodes(GeneralDerivedCRS.class)) {
+            final CoordinateReferenceSystem crs;
+            try {
+                crs = factory.createCoordinateReferenceSystem(code);
+            } catch (FactoryException e) {
+                continue; // Ignore and inspect the next element.
+            }
+            if (crs instanceof GeneralDerivedCRS) {
+                final GeographicBoundingBox candidate = CRS.getGeographicBoundingBox(crs);
+                if (candidate != null) {
+                    final String name = ((GeneralDerivedCRS) crs).getConversionFromBase().getMethod().getName().getCode();
+                    DefaultGeographicBoundingBox validity = domainOfValidity.get(name);
+                    if (validity == null) {
+                        validity = new DefaultGeographicBoundingBox(candidate);
+                        domainOfValidity.put(name, validity);
+                    } else {
+                        validity.add(candidate);
                     }
                 }
             }
         }
-        return new OrderedRow(row, categoryIndex);
+        return domainOfValidity;
+    }
+
+    /**
+     * Returns the domain of validity for the given operation method.
+     * If no domain of validity is found, returns {@code null}.
+     */
+    private DefaultGeographicBoundingBox getDomainOfValidity(final OperationMethod method)
{
+        DefaultGeographicBoundingBox validity = null;
+        for (final GenericName name : method.getAlias()) {
+            final String tip = name.tip().toString();
+            final DefaultGeographicBoundingBox candidate = domainOfValidity.get(tip);
+            if (candidate != null) {
+                if (validity == null) {
+                    validity = new DefaultGeographicBoundingBox(candidate);
+                } else {
+                    validity.add(candidate);
+                }
+            }
+        }
+        return validity;
+    }
+
+    /**
+     * Returns the string representation of the given parameter default value,
+     * or an empty string (never {@code null}) if none.
+     */
+    private String getDefaultValue(final ParameterDescriptor<?> param, final String
unit) {
+        Object defaultValue = param.getDefaultValue();
+        if (defaultValue != null) {
+            if (defaultValue instanceof Number) {
+                // Trim the fractional part if unnecessary (e.g. "0.0" to "0").
+                defaultValue = Numbers.narrowestNumber((Number) defaultValue);
+            } else if (defaultValue instanceof String) {
+                return (String) defaultValue;
+            }
+        } else if (param.getMinimumOccurs() == 0) {
+            if (ArraysExt.contains(defaultToLatitudeOfOrigin, param)) {
+                return "latitude of origin";
+            } else if (ArraysExt.contains(defaultToStandardParallel1, param)) {
+                return "standard parallel 1";
+            } else if (ArraysExt.contains(defaultToAzimuth, param)) {
+                return "Azimuth of initial line";
+            } else if (param.getValueClass() == Boolean.class) {
+                defaultValue = Boolean.FALSE;
+            }
+        }
+        return (defaultValue != null) ? defaultValue + unit : "";
+    }
+
+    /**
+     * Returns the string representation of the given parameter unit,
+     * or an empty string (never {@code null}) if none.
+     */
+    @Workaround(library="JSR-275", version="0.9.3")
+    private static String getUnit(final ParameterDescriptor<?> param) {
+        final Unit<?> unit = param.getUnit();
+        if (unit != null) {
+            final String text;
+            try {
+                text = unit.toString();
+                if (!text.isEmpty()) {
+                    return text.equals("deg") ? "°" : " " + text;
+                }
+            } catch (IllegalArgumentException e) {
+                // Workaround for JSR-275 implementation bug.
+                // Do nothing, we will returns the empty string below.
+            }
+        }
+        return "";
+    }
+
+    /**
+     * Returns a code for sorting methods in categories.
+     */
+    private static int category(final OperationMethod method) {
+        final Class<?> c = ((DefaultOperationMethod) method).getOperationType();
+        if (Projection    .class.isAssignableFrom(c)) return PROJECTION;
+        if (Conversion    .class.isAssignableFrom(c)) return CONVERSION;
+        if (Transformation.class.isAssignableFrom(c)) return TRANSFORMATION;
+        return 0;
     }
 
     /**
-     * A row implementation sorted by category before to be sorted by name. This implementation
-     * is used for sorting the operation methods in the order to be show on the HTML output
page.
-     * First, the operations are sorted by categories according the order of elements in
the
-     * {@link #categories} array. For each operation of the same category, methods are
-     * sorted by alphabetical order.
+     * Returns the first EPSG code found in the given collection, or {@code null} if none.
      */
-    private static final class OrderedRow extends Row {
-        /** The category index to use for sorting rows. */
-        private final int categoryIndex;
+    private static String getFirstEpsgCode(final Iterable<? extends Identifier> identifiers)
{
+        for (final Identifier id : identifiers) {
+            if (Constants.EPSG.equalsIgnoreCase(id.getCodeSpace())) {
+                return id.getCode();
+            }
+        }
+        return null;
+    }
 
-        /** Creates a new row as a copy of the given row.*/
-        OrderedRow(final Row toCopy, final int categoryIndex) {
-            super(toCopy);
-            this.categoryIndex = categoryIndex;
+    /**
+     * Returns an identifier to use for HREF.
+     */
+    private static String getAnchor(final OperationMethod method) {
+        String id = getFirstEpsgCode(method.getIdentifiers());
+        if (id == null) {
+            id = method.getName().getCode();
         }
+        return id;
+    }
 
-        /** Compares by category, then compares by name. */
-        @Override public int compareTo(final Row o) {
-            final int c = categoryIndex - ((OrderedRow) o).categoryIndex;
-            return (c != 0) ? c : super.compareTo(o);
+    /**
+     * Returns a string representation of the given range, or {@code null} if none.
+     */
+    private String toLocalizedString(final Range<?> range) {
+        return (range != null) ? rangeFormat.format(range) : null;
+    }
+
+    /**
+     * Returns the superscript character for the given number.
+     * This is used for footnotes.
+     */
+    private static char toSuperScript(final int index) {
+        if (index >= 10) {
+            throw new IllegalArgumentException("Too many footnotes.");
         }
+        return Characters.toSuperScript((char) (index + '0'));
     }
 }

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java?rev=1672784&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java
(added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java
[UTF-8] Fri Apr 10 22:59:40 2015
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.test;
+
+import java.util.Locale;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Deprecable;
+
+
+/**
+ * Base class of all classes used to generate HTML pages to be published on
+ * the <a href="http://sis.apache.org/">http://sis.apache.org/</a> web site.
+ *
+ * <p>This class creates files in the current default directory. It is user's responsibility
+ * to move the files to the appropriate Apache SIS {@code "content/"} site directory.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public abstract class HTMLGenerator implements AutoCloseable {
+    /**
+     * The encoding of the files to generate.
+     */
+    private static final String ENCODING = "UTF-8";
+
+    /**
+     * The language to use for the reports to generate.
+     *
+     * @see #toLocalizedString(InternationalString)
+     */
+    protected static final Locale LOCALE = Locale.US;
+
+    /**
+     * The number of space to add or remove in the {@linkplain #margin}
+     * when new HTML elements are opened or closed.
+     */
+    private static final int INDENTATION = 2;
+
+    /**
+     * Where to write the HTML page.
+     */
+    private final BufferedWriter out;
+
+    /**
+     * The spaces to write in the margin before every new line.
+     * The number of spaces will increase by the indentation amount when new elements are
opened.
+     *
+     * @see #INDENTATION
+     */
+    private String margin = "";
+
+    /**
+     * Creates a new instance which will write in the given file.
+     * This constructor immediately writes the HTML header up to the {@code <h1>} line,
inclusive.
+     *
+     * @param  filename The name of the file where to write.
+     * @param  title The document title and the title to write as {@code <h1>} line.
+     * @throws IOException if the file can not be created (e.g. because it already exists).
+     */
+    protected HTMLGenerator(final String filename, final String title) throws IOException
{
+        final File file = new File(filename);
+        if (file.exists()) {
+            throw new IOException("File " + file.getAbsolutePath() + " already exists.");
+        }
+        out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), ENCODING));
+        out.write("<!DOCTYPE html>");
+        out.newLine();
+        out.write("<!--");
+        out.newLine();
+        out.write("  This page is automatically generated by the following class in the test
directory:");
+        out.newLine();
+        out.write("  ");
+        out.write(getClass().getCanonicalName());
+        out.newLine();
+        out.write("-->");
+        out.newLine();
+        open   ("html");
+        open   ("head");
+        out.write(margin);
+        out.write("<meta charset=\"" + ENCODING + "\"/>");
+        out.newLine();
+        println("title", CharSequences.replace(title, "™", ""));
+        open   ("style type=\"text/css\" media=\"all\"");
+        println("@import url(\"./reports.css\");");
+        close  ("style");
+        close  ("head");
+        open   ("body");
+        println("h1", title);
+    }
+
+    /**
+     * Escapes the {@code &}, {@code <} and {@code >} characters.
+     *
+     * @param  text The text to escape, or {@code null}.
+     * @return The escaped text, or {@code null} if the given text was null.
+     */
+    protected static CharSequence escape(CharSequence text) {
+        text = CharSequences.replace(text, "&", "&amp;");
+        text = CharSequences.replace(text, "<", "&lt;");
+        text = CharSequences.replace(text, ">", "&gt;");
+        return text;
+    }
+
+    /**
+     * Opens a new HTML element and increase the indentation.
+     *
+     * @param  element The HTML element without brackets (e.g. {@code "h2"}).
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    protected final void open(final String element) throws IOException {
+        out.write(margin);
+        out.write('<');
+        out.write(element);
+        out.write('>');
+        out.newLine();
+        margin = CharSequences.spaces(margin.length() + INDENTATION).toString();
+    }
+
+    /**
+     * Closes a HTML element an opens a new one on the same line.
+     *
+     * @param  previous The HTML element to close, without brackets.
+     * @param  element  The HTML element without brackets (e.g. {@code "h2"}).
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    protected final void reopen(final String previous, final String element) throws IOException
{
+        out.write(CharSequences.spaces(margin.length() - INDENTATION).toString());
+        out.write("</");
+        out.write(previous);
+        out.write("><");
+        out.write(element);
+        out.write('>');
+        out.newLine();
+    }
+
+    /**
+     * Closes a HTML element and decrease the indentation. The {@code previous} argument
must matches
+     * the argument given to the last call to {@link #open(String)}.
+     *
+     * @param  previous The HTML element without brackets (e.g. {@code "h2"}).
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    protected final void close(final String previous) throws IOException {
+        margin = CharSequences.spaces(margin.length() - INDENTATION).toString();
+        out.write(margin);
+        out.write("</");
+        out.write(previous);
+        out.write('>');
+        out.newLine();
+    }
+
+    /**
+     * Writes the given text in the given HTML element.
+     * {@code &}, {@code <} and {@code >} characters in {@code value} will be escaped.
+     *
+     * @param  element The HTML element without brackets (e.g. {@code "h1"}).
+     * @param  value The text to write, or {@code null} for none.
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    protected final void println(final String element, final CharSequence value) throws IOException
{
+        printlnHTML(element, escape(value));
+    }
+
+    /**
+     * Writes the given text in the given HTML element without escaping the characters.
+     * This method can be invoked when the given {@code value} is already valid HTML
+     *
+     * @param  element The HTML element without brackets (e.g. {@code "h1"}).
+     * @param  value The text to write, or {@code null} for none.
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    protected final void printlnHTML(final String element, final CharSequence value) throws
IOException {
+        out.write(margin);
+        out.write('<');
+        out.write(element);
+        out.write('>');
+        if (value != null) {
+            out.write(value.toString());
+        }
+        out.write("</");
+        final int s = element.indexOf(' ');
+        out.write(element, 0, (s >= 0) ? s : element.length());
+        out.write('>');
+        out.newLine();
+    }
+
+    /**
+     * Writes the given text on its own line, then write EOL sequence.
+     * {@code &}, {@code <} and {@code >} characters in {@code value} will be escaped.
+     *
+     * @param  value The text to write, or {@code null} if none.
+     * @throws IOException if an error occurred while writing to the file.
+     */
+    protected final void println(final CharSequence value) throws IOException {
+        if (value != null) {
+            out.write(margin);
+            out.write(escape(value).toString());
+            out.newLine();
+        }
+    }
+
+    /**
+     * Closes the HTML generator.
+     *
+     * @throws IOException if an error occurred while closing the output file.
+     */
+    @Override
+    public void close() throws IOException {
+        close("body");
+        close("html");
+        out.close();
+    }
+
+    /**
+     * Returns the localized version of the given string, or {@code null} if none.
+     *
+     * @param  text The text to localize, or {@code null}.
+     * @return The localized test, or {@code null}.
+     *
+     * @see #LOCALE
+     */
+    protected static String toLocalizedString(final InternationalString text) {
+        return (text != null) ? text.toString(LOCALE) : null;
+    }
+
+    /**
+     * Returns {@code true} if the given object is deprecated.
+     *
+     * @param  object The object to test.
+     * @return {@code true} if the given object is deprecated.
+     */
+    protected static boolean isDeprecated(final Object object) {
+        return (object instanceof Deprecable) && ((Deprecable) object).isDeprecated();
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/HTMLGenerator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message