From commits-return-6444-apmail-sis-commits-archive=sis.apache.org@sis.apache.org Wed Oct 7 14:00:01 2015 Return-Path: X-Original-To: apmail-sis-commits-archive@www.apache.org Delivered-To: apmail-sis-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id E183D17BED for ; Wed, 7 Oct 2015 14:00:01 +0000 (UTC) Received: (qmail 95651 invoked by uid 500); 7 Oct 2015 14:00:01 -0000 Delivered-To: apmail-sis-commits-archive@sis.apache.org Received: (qmail 95620 invoked by uid 500); 7 Oct 2015 14:00:01 -0000 Mailing-List: contact commits-help@sis.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sis-dev@sis.apache.org Delivered-To: mailing list commits@sis.apache.org Received: (qmail 95611 invoked by uid 99); 7 Oct 2015 14:00:01 -0000 Received: from Unknown (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 07 Oct 2015 14:00:01 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 29E681A22E1 for ; Wed, 7 Oct 2015 14:00:01 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.79 X-Spam-Level: * X-Spam-Status: No, score=1.79 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, T_RP_MATCHES_RCVD=-0.01] autolearn=disabled Received: from mx1-us-west.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id ZECVplS63wXK for ; Wed, 7 Oct 2015 13:59:49 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-us-west.apache.org (ASF Mail Server at mx1-us-west.apache.org) with ESMTP id 3F7DD20F7B for ; Wed, 7 Oct 2015 13:59:49 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id AF24EE0760 for ; Wed, 7 Oct 2015 13:59:48 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id A264F3A0983 for ; Wed, 7 Oct 2015 13:59:48 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1707309 [2/3] - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/ core/sis-metad... Date: Wed, 07 Oct 2015 13:59:48 -0000 To: commits@sis.apache.org From: desruisseaux@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20151007135948.A264F3A0983@svn01-us-west.apache.org> Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -57,56 +57,32 @@ import static org.apache.sis.math.MathFu * @author Martin Desruisseaux (Geomatys) * @author Rémi Maréchal (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module * * @see Mercator * @see ObliqueMercator */ -public class TransverseMercator extends NormalizedProjection { +public class TransverseMercator extends ConformalProjection { /** * For cross-version compatibility. */ - private static final long serialVersionUID = -4717976245811852528L; + private static final long serialVersionUID = 8167193470189463354L; /** - * Whether to use the original formulas a published by EPSG, or their form modified using trigonometric identities. - * The modified form uses trigonometric identifies for reducing the amount of calls to the {@link Math#sin(double)} - * and similar method. The identities used are: + * Coefficients in the series expansion of the forward projection, + * depending only on {@linkplain #excentricity excentricity} value. + * The series expansion is of the following form: * - *
    - *
  • sin(2θ) = 2⋅sinθ⋅cosθ
  • - *
  • cos(2θ) = cos²θ - sin²θ
  • - *
  • sin(3θ) = (3 - 4⋅sin²θ)⋅sinθ
  • - *
  • cos(3θ) = (4⋅cos³θ) - 3⋅cosθ
  • - *
  • sin(4θ) = (4 - 8⋅sin²θ)⋅sinθ⋅cosθ
  • - *
  • cos(4θ) = (8⋅cos⁴θ) - (8⋅cos²θ) + 1
  • - *
+ *
cf₂⋅f(2θ) + cf₄⋅f(4θ) + cf₆⋅f(6θ) + cf₈⋅f(8θ)
* - * Hyperbolic formulas: - *
    - *
  • sinh(2θ) = 2⋅sinhθ⋅coshθ
  • - *
  • cosh(2θ) = cosh²θ + sinh²θ = 2cosh²θ - 1 = 1 + 2sinh²θ
  • - *
  • sinh(3θ) = (3 + 4⋅sinh²θ)⋅sinhθ
  • - *
  • cosh(3θ) = ((4⋅cosh²θ) - 3)coshθ
  • - *
  • sinh(4θ) = (1 + 2⋅sinh²θ)⋅4.sinhθ⋅coshθ - * = 4.cosh(2θ).sinhθ⋅coshθ
  • - *
  • cosh(4θ) = (8⋅cosh⁴θ) - (8⋅cosh²θ) + 1 - * = 8.cosh²θ(cosh²θ - 1) + 1 - * = 8.cosh²(θ).sinh²(θ) + 1 - * = 2.sinh²(2θ) + 1
  • - *
+ * Those coefficients are named h₁, h₂, h₃ and h₄ in §1.3.5.1 of + * IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015. * - * Note that since this boolean is static final, the compiler should exclude the code in the branch that is never - * executed (no need to comment-out that code). - */ - private static final boolean ORIGINAL_FORMULA = false; - - /** - * Internal coefficients for computation, depending only on value of excentricity. - * Defined in §1.3.5.1 of IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015. + *

Consider those fields as final! + * They are not final only for the purpose of {@link #computeCoefficients()}.

*/ - private final double h1, h2, h3, h4, ih1, ih2, ih3, ih4; + private transient double cf2, cf4, cf6, cf8; /** * Creates a Transverse Mercator projection from the given parameters. @@ -160,7 +136,6 @@ public class TransverseMercator extends * Opportunistically use double-double arithmetic for computation of B since we will store * it in the denormalization matrix, and there is no sine/cosine functions involved here. */ - final double n; final DoubleDouble B; { // For keeping the 't' variable locale. /* @@ -169,35 +144,16 @@ public class TransverseMercator extends */ final DoubleDouble t = initializer.axisLengthRatio(); // t = b/a t.ratio_1m_1p(); // t = (1 - t) / (1 + t) - n = t.doubleValue(); + computeCoefficients(t.doubleValue()); /* * Compute B = (1 + n²/4 + n⁴/64) / (1 + n) */ B = new DoubleDouble(t); // B = n B.square(); B.series(1, 0.25, 1./64); // B = (1 + n²/4 + n⁴/64) - t.add(1,0); + t.add(1, 0); B.divide(t); // B = (1 + n²/4 + n⁴/64) / (1 + n) } - final double n2 = n * n; - final double n3 = n2 * n; - final double n4 = n2 * n2; - /* - * Coefficients for direct projection. - * Add the smallest values first in order to reduce rounding errors. - */ - h1 = ( 41. / 180)*n4 + ( 5. / 16)*n3 + (-2. / 3)*n2 + n/2; - h2 = ( 557. / 1440)*n4 + (-3. / 5)*n3 + (13. / 48)*n2; - h3 = ( -103. / 140)*n4 + (61. / 240)*n3; - h4 = (49561. / 161280)*n4; - /* - * Coefficients for inverse projection. - * Add the smallest values first in order to reduce rounding errors. - */ - ih1 = ( -1. / 360)*n4 + (37. / 96)*n3 + (-2. / 3)*n2 + n/2; - ih2 = (-437. / 1440)*n4 + ( 1. / 15)*n3 + ( 1. / 48)*n2; - ih3 = ( -37. / 840)*n4 + (17. / 480)*n3; - ih4 = (4397. / 161280)*n4; /* * Compute M₀ = B⋅(ξ₁ + ξ₂ + ξ₃ + ξ₄) and negate in anticipation for what will be needed * in the denormalization matrix. We opportunistically use double-double arithmetic but @@ -211,10 +167,10 @@ public class TransverseMercator extends final double Q = asinh(tan(φ0)) - excentricity * atanh(excentricity * sin(φ0)); final double β = atan(sinh(Q)); final DoubleDouble M0 = new DoubleDouble(); - M0.value = h4 * sin(8*β) - + h3 * sin(6*β) - + h2 * sin(4*β) - + h1 * sin(2*β) + M0.value = cf8 * sin(8*β) + + cf6 * sin(6*β) + + cf4 * sin(4*β) + + cf2 * sin(2*β) + β; M0.multiply(B); M0.negate(); @@ -235,6 +191,81 @@ public class TransverseMercator extends final MatrixSIS denormalize = context.getMatrix(false); denormalize.convertBefore(0, B, null); denormalize.convertBefore(1, B, M0); + /* + * When rewriting equations using trigonometric identities, some constants appear. + * For example sin(2θ) = 2⋅sinθ⋅cosθ, so we can factor out the 2 constant into the + * corresponding 'c' field. Note: this factorization can only be performed after + * the constructor finished to compute other constants. + */ + if (ALLOW_TRIGONOMETRIC_IDENTITIES) { + // Multiplication by powers of 2 does not bring any additional rounding error. + cf4 *= 4; ci4 *= 4; + cf6 *= 16; ci6 *= 16; + cf8 *= 64; ci8 *= 64; + } + } + + /** + * Automatically invoked after deserialization for restoring transient fields. + */ + @Override + final void computeCoefficients() { + /* + * Double-double precision is necessary for computing 'n', otherwise we have rounding errors + * in the 3 last digits. Note that we still have sometime a 1 ULP difference compared to the + * 'n' value at serialization time. + */ + final DoubleDouble t = new DoubleDouble(1, 0); + t.subtract(excentricitySquared, 0); + t.sqrt(); + t.ratio_1m_1p(); + computeCoefficients(t.doubleValue()); + if (ALLOW_TRIGONOMETRIC_IDENTITIES) { + // Same scaling than in the constructor. + cf4 *= 4; ci4 *= 4; + cf6 *= 16; ci6 *= 16; + cf8 *= 64; ci8 *= 64; + } + } + + /** + * Computes the transient fields after construction or deserialization. + * The n parameter is defined by the EPSG guide as: + * + *
n = f / (2-f)
+ * + * Where f is the flattening factor (1 - b/a). This equation can be rewritten as: + * + *
n = (1 - b/a) / (1 + b/a)
+ * + * As much as possible, b/a should be computed from the map projection parameters. + * However if those parameters are not available anymore, then they can be computed + * from the excentricity as: + * + *
b/a = √(1 - ℯ²)
+ * + * @param n The value of {@code f / (2-f)} where {@code f} is the flattening factor. + */ + private void computeCoefficients(final double n) { + final double n2 = n * n; + final double n3 = n2 * n; + final double n4 = n2 * n2; + /* + * Coefficients for the forward projections. + * Add the smallest values first in order to reduce rounding errors. + */ + cf2 = ( 41. / 180)*n4 + ( 5. / 16)*n3 + (-2. / 3)*n2 + n/2; + cf4 = ( 557. / 1440)*n4 + (-3. / 5)*n3 + (13. / 48)*n2; + cf6 = ( -103. / 140)*n4 + (61. / 240)*n3; + cf8 = (49561. / 161280)*n4; + /* + * Coefficients for the inverse projections. + * Add the smallest values first in order to reduce rounding errors. + */ + ci2 = ( -1. / 360)*n4 + (37. / 96)*n3 + (-2. / 3)*n2 + n/2; + ci4 = (-437. / 1440)*n4 + ( 1. / 15)*n3 + ( 1. / 48)*n2; + ci6 = ( -37. / 840)*n4 + (17. / 480)*n3; + ci8 = (4397. / 161280)*n4; } /** @@ -242,19 +273,15 @@ public class TransverseMercator extends */ TransverseMercator(final TransverseMercator other) { super(other); - h1 = other. h1; - h2 = other. h2; - h3 = other. h3; - h4 = other. h4; - ih1 = other.ih1; - ih2 = other.ih2; - ih3 = other.ih3; - ih4 = other.ih4; + cf2 = other.cf2; + cf4 = other.cf4; + cf6 = other.cf6; + cf8 = other.cf8; } /** * Returns the sequence of normalization → {@code this} → denormalization transforms - * as a whole. The transform returned by this method except (longitude, latitude) + * as a whole. The transform returned by this method expects (longitude, latitude) * coordinates in degrees and returns (x,y) coordinates in metres. * *

The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid @@ -286,15 +313,13 @@ public class TransverseMercator extends final double[] dstPts, final int dstOff, final boolean derivate) throws ProjectionException { - final double λ = srcPts[srcOff]; - final double φ = srcPts[srcOff + 1]; - - final double ℯsinφ = excentricity * sin(φ); - final double Q = asinh(tan(φ)) - atanh(ℯsinφ) * excentricity; + final double λ = srcPts[srcOff ]; + final double φ = srcPts[srcOff+1]; final double sinλ = sin(λ); + final double ℯsinφ = sin(φ) * excentricity; + final double Q = asinh(tan(φ)) - atanh(ℯsinφ) * excentricity; final double coshQ = cosh(Q); final double η0 = atanh(sinλ / coshQ); - /* * Original formula: η0 = atanh(sin(λ) * cos(β)) where * cos(β) = cos(atan(sinh(Q))) @@ -307,16 +332,15 @@ public class TransverseMercator extends */ final double coshη0 = cosh(η0); final double ξ0 = asin(tanh(Q) * coshη0); - /* * Compute sin(2⋅ξ₀), sin(4⋅ξ₀), sin(6⋅ξ₀), sin(8⋅ξ₀) and same for cos, but using the following * trigonometric identities in order to reduce the number of calls to Math.sin and cos methods. */ - final double sin_2ξ0, sin_4ξ0, sin_6ξ0, sin_8ξ0, - cos_2ξ0, cos_4ξ0, cos_6ξ0, cos_8ξ0; - if (ORIGINAL_FORMULA) { - sin_2ξ0 = sin(2*ξ0); - cos_2ξ0 = cos(2*ξ0); + final double sin_2ξ0 = sin(2*ξ0); + final double cos_2ξ0 = cos(2*ξ0); + final double sin_4ξ0, sin_6ξ0, sin_8ξ0, + cos_4ξ0, cos_6ξ0, cos_8ξ0; + if (!ALLOW_TRIGONOMETRIC_IDENTITIES) { sin_4ξ0 = sin(4*ξ0); cos_4ξ0 = cos(4*ξ0); sin_6ξ0 = sin(6*ξ0); @@ -324,28 +348,25 @@ public class TransverseMercator extends sin_8ξ0 = sin(8*ξ0); cos_8ξ0 = cos(8*ξ0); } else { - sin_2ξ0 = sin(2*ξ0); // sin(2⋅ξ₀); - cos_2ξ0 = cos(2*ξ0); // cos(2⋅ξ₀) final double sin2 = sin_2ξ0 * sin_2ξ0; final double cos2 = cos_2ξ0 * cos_2ξ0; - sin_4ξ0 = 2 * sin_2ξ0 * cos_2ξ0; // sin(4⋅ξ₀) - cos_4ξ0 = cos2 - sin2; // cos(4⋅ξ₀) - sin_6ξ0 = (3 - 4*sin2) * sin_2ξ0; // sin(6⋅ξ₀) - cos_6ξ0 = (4*cos2 - 3) * cos_2ξ0; // cos(6⋅ξ₀) - sin_8ξ0 = 4*cos_4ξ0 * (sin_2ξ0 * cos_2ξ0); // sin(8⋅ξ₀) - cos_8ξ0 = 1 - 2*sin_4ξ0*sin_4ξ0; // cos(8⋅ξ₀) + sin_4ξ0 = sin_2ξ0 * cos_2ξ0; assert identityEquals(sin_4ξ0, sin(4*ξ0) / 2) : ξ0; + cos_4ξ0 = (cos2 - sin2) * 0.5; assert identityEquals(cos_4ξ0, cos(4*ξ0) / 2) : ξ0; + sin_6ξ0 = (0.75 - sin2) * sin_2ξ0; assert identityEquals(sin_6ξ0, sin(6*ξ0) / 4) : ξ0; + cos_6ξ0 = (cos2 - 0.75) * cos_2ξ0; assert identityEquals(cos_6ξ0, cos(6*ξ0) / 4) : ξ0; + sin_8ξ0 = sin_4ξ0 * cos_4ξ0; assert identityEquals(sin_8ξ0, sin(8*ξ0) / 8) : ξ0; + cos_8ξ0 = 0.125 - sin_4ξ0 * sin_4ξ0; assert identityEquals(cos_8ξ0, cos(8*ξ0) / 8) : ξ0; } - /* * Compute sinh(2⋅ξ₀), sinh(4⋅ξ₀), sinh(6⋅ξ₀), sinh(8⋅ξ₀) and same for cosh, but using the following * hyperbolic identities in order to reduce the number of calls to Math.sinh and cosh methods. * Note that the formulas are very similar to the above ones, with only some signs reversed. */ - final double sinh_2η0, sinh_4η0, sinh_6η0, sinh_8η0, - cosh_2η0, cosh_4η0, cosh_6η0, cosh_8η0; - if (ORIGINAL_FORMULA) { - sinh_2η0 = sinh(2*η0); - cosh_2η0 = cosh(2*η0); + final double sinh_2η0 = sinh(2*η0); + final double cosh_2η0 = cosh(2*η0); + final double sinh_4η0, sinh_6η0, sinh_8η0, + cosh_4η0, cosh_6η0, cosh_8η0; + if (!ALLOW_TRIGONOMETRIC_IDENTITIES) { sinh_4η0 = sinh(4*η0); cosh_4η0 = cosh(4*η0); sinh_6η0 = sinh(6*η0); @@ -353,18 +374,15 @@ public class TransverseMercator extends sinh_8η0 = sinh(8*η0); cosh_8η0 = cosh(8*η0); } else { - sinh_2η0 = sinh(2*η0); // sinh(2⋅η₀); - cosh_2η0 = cosh(2*η0); // cosh(2⋅η₀) final double sinh2 = sinh_2η0 * sinh_2η0; final double cosh2 = cosh_2η0 * cosh_2η0; - sinh_4η0 = 2 * sinh_2η0 * cosh_2η0; // sinh(4⋅η₀) - cosh_4η0 = cosh2 + sinh2; // cosh(4⋅η₀) - sinh_6η0 = (3 + 4*sinh2) * sinh_2η0; // sinh(6⋅η₀) - cosh_6η0 = (4*cosh2 - 3) * cosh_2η0; // cosh(6⋅η₀) - sinh_8η0 = 4*cosh_4η0 * (sinh_2η0 * cosh_2η0); // sinh(8⋅η₀) - cosh_8η0 = 1 + 2*sinh_4η0*sinh_4η0; // cosh(8⋅η₀) + cosh_4η0 = (cosh2 + sinh2) * 0.5; assert identityEquals(cosh_4η0, cosh(4*η0) / 2) : η0; + sinh_4η0 = cosh_2η0 * sinh_2η0; assert identityEquals(sinh_4η0, sinh(4*η0) / 2) : η0; + cosh_6η0 = cosh_2η0 * (cosh2 - 0.75); assert identityEquals(cosh_6η0, cosh(6*η0) / 4) : η0; + sinh_6η0 = sinh_2η0 * (sinh2 + 0.75); assert identityEquals(sinh_6η0, sinh(6*η0) / 4) : η0; + cosh_8η0 = sinh_4η0 * sinh_4η0 + 0.125; assert identityEquals(cosh_8η0, cosh(8*η0) / 8) : η0; + sinh_8η0 = sinh_4η0 * cosh_4η0; assert identityEquals(sinh_8η0, sinh(8*η0) / 8) : η0; } - /* * Assuming that (λ, φ) ↦ Proj((λ, φ)) * where Proj is defined by: Proj((λ, φ)) : (η(λ, φ), ξ(λ, φ)). @@ -372,36 +390,35 @@ public class TransverseMercator extends * => (λ, φ) ↦ (η(λ, φ), ξ(λ, φ)). */ //-- ξ(λ, φ) - final double ξ = h4 * sin_8ξ0 * cosh_8η0 - + h3 * sin_6ξ0 * cosh_6η0 - + h2 * sin_4ξ0 * cosh_4η0 - + h1 * sin_2ξ0 * cosh_2η0 + final double ξ = cf8 * sin_8ξ0 * cosh_8η0 + + cf6 * sin_6ξ0 * cosh_6η0 + + cf4 * sin_4ξ0 * cosh_4η0 + + cf2 * sin_2ξ0 * cosh_2η0 + ξ0; //-- η(λ, φ) - final double η = h4 * cos_8ξ0 * sinh_8η0 - + h3 * cos_6ξ0 * sinh_6η0 - + h2 * cos_4ξ0 * sinh_4η0 - + h1 * cos_2ξ0 * sinh_2η0 + final double η = cf8 * cos_8ξ0 * sinh_8η0 + + cf6 * cos_6ξ0 * sinh_6η0 + + cf4 * cos_4ξ0 * sinh_4η0 + + cf2 * cos_2ξ0 * sinh_2η0 + η0; if (dstPts != null) { - dstPts[dstOff ] = η; - dstPts[dstOff + 1] = ξ; + dstPts[dstOff ] = η; + dstPts[dstOff+1] = ξ; } - if (!derivate) { return null; } - final double cosλ = cos(λ); //-- λ - final double cosφ = cos(φ); //-- φ - final double cosh2Q = coshQ * coshQ; //-- Q + final double cosλ = cos(λ); //-- λ + final double cosφ = cos(φ); //-- φ + final double cosh2Q = coshQ * coshQ; //-- Q final double sinhQ = sinh(Q); final double tanhQ = tanh(Q); - final double cosh2Q_sin2λ = cosh2Q - sinλ * sinλ; //-- Qλ - final double sinhη0 = sinh(η0); //-- η0 - final double sqrt1_thQchη0 = sqrt(1 - tanhQ * tanhQ * coshη0 * coshη0); //-- Qη0 + final double cosh2Q_sin2λ = cosh2Q - (sinλ * sinλ); //-- Qλ + final double sinhη0 = sinh(η0); //-- η0 + final double sqrt1_thQchη0 = sqrt(1 - (tanhQ * tanhQ) * (coshη0 * coshη0)); //-- Qη0 //-- dQ_dλ = 0; final double dQ_dφ = 1 / cosφ - excentricitySquared * cosφ / (1 - ℯsinφ * ℯsinφ); @@ -411,11 +428,10 @@ public class TransverseMercator extends final double dξ0_dλ = sinhQ * sinhη0 * cosλ / (cosh2Q_sin2λ * sqrt1_thQchη0); final double dξ0_dφ = (dQ_dφ * coshη0 / cosh2Q + dη0_dφ * sinhη0 * tanhQ) / sqrt1_thQchη0; - /* * Assuming that Jac(Proj((λ, φ))) is the Jacobian matrix of Proj((λ, φ)) function. * - * So derivative Proj((λ, φ)) is defined by: + * So the derivative of Proj((λ, φ)) is defined by: * ┌ ┐ * │ dη(λ, φ) / dλ, dη(λ, φ) / dφ │ * Jac = │ │ @@ -424,31 +440,31 @@ public class TransverseMercator extends */ //-- dξ(λ, φ) / dλ final double dξ_dλ = dξ0_dλ - + 2 * (h1 * (dξ0_dλ * cos_2ξ0 * cosh_2η0 + dη0_dλ * sinh_2η0 * sin_2ξ0) - + 3 * h3 * (dξ0_dλ * cos_6ξ0 * cosh_6η0 + dη0_dλ * sinh_6η0 * sin_6ξ0) - + 2 * (h2 * (dξ0_dλ * cos_4ξ0 * cosh_4η0 + dη0_dλ * sinh_4η0 * sin_4ξ0) - + 2 * h4 * (dξ0_dλ * cos_8ξ0 * cosh_8η0 + dη0_dλ * sinh_8η0 * sin_8ξ0))); + + 2 * (cf2 * (dξ0_dλ * cos_2ξ0 * cosh_2η0 + dη0_dλ * sinh_2η0 * sin_2ξ0) + + 3 * cf6 * (dξ0_dλ * cos_6ξ0 * cosh_6η0 + dη0_dλ * sinh_6η0 * sin_6ξ0) + + 2 * (cf4 * (dξ0_dλ * cos_4ξ0 * cosh_4η0 + dη0_dλ * sinh_4η0 * sin_4ξ0) + + 2 * cf8 * (dξ0_dλ * cos_8ξ0 * cosh_8η0 + dη0_dλ * sinh_8η0 * sin_8ξ0))); //-- dξ(λ, φ) / dφ final double dξ_dφ = dξ0_dφ - + 2 * (h1 * (dξ0_dφ * cos_2ξ0 * cosh_2η0 + dη0_dφ * sinh_2η0 * sin_2ξ0) - + 3 * h3 * (dξ0_dφ * cos_6ξ0 * cosh_6η0 + dη0_dφ * sinh_6η0 * sin_6ξ0) - + 2 * (h2 * (dξ0_dφ * cos_4ξ0 * cosh_4η0 + dη0_dφ * sinh_4η0 * sin_4ξ0) - + 2 * h4 * (dξ0_dφ * cos_8ξ0 * cosh_8η0 + dη0_dφ * sinh_8η0 * sin_8ξ0))); + + 2 * (cf2 * (dξ0_dφ * cos_2ξ0 * cosh_2η0 + dη0_dφ * sinh_2η0 * sin_2ξ0) + + 3 * cf6 * (dξ0_dφ * cos_6ξ0 * cosh_6η0 + dη0_dφ * sinh_6η0 * sin_6ξ0) + + 2 * (cf4 * (dξ0_dφ * cos_4ξ0 * cosh_4η0 + dη0_dφ * sinh_4η0 * sin_4ξ0) + + 2 * cf8 * (dξ0_dφ * cos_8ξ0 * cosh_8η0 + dη0_dφ * sinh_8η0 * sin_8ξ0))); //-- dη(λ, φ) / dλ final double dη_dλ = dη0_dλ - + 2 * (h1 * (dη0_dλ * cosh_2η0 * cos_2ξ0 - dξ0_dλ * sin_2ξ0 * sinh_2η0) - + 3 * h3 * (dη0_dλ * cosh_6η0 * cos_6ξ0 - dξ0_dλ * sin_6ξ0 * sinh_6η0) - + 2 * (h2 * (dη0_dλ * cosh_4η0 * cos_4ξ0 - dξ0_dλ * sin_4ξ0 * sinh_4η0) - + 2 * h4 * (dη0_dλ * cosh_8η0 * cos_8ξ0 - dξ0_dλ * sin_8ξ0 * sinh_8η0))); + + 2 * (cf2 * (dη0_dλ * cosh_2η0 * cos_2ξ0 - dξ0_dλ * sin_2ξ0 * sinh_2η0) + + 3 * cf6 * (dη0_dλ * cosh_6η0 * cos_6ξ0 - dξ0_dλ * sin_6ξ0 * sinh_6η0) + + 2 * (cf4 * (dη0_dλ * cosh_4η0 * cos_4ξ0 - dξ0_dλ * sin_4ξ0 * sinh_4η0) + + 2 * cf8 * (dη0_dλ * cosh_8η0 * cos_8ξ0 - dξ0_dλ * sin_8ξ0 * sinh_8η0))); //-- dη(λ, φ) / dφ final double dη_dφ = dη0_dφ - + 2 * (h1 * (dη0_dφ * cosh_2η0 * cos_2ξ0 - dξ0_dφ * sin_2ξ0 * sinh_2η0) - + 3 * h3 * (dη0_dφ * cosh_6η0 * cos_6ξ0 - dξ0_dφ * sin_6ξ0 * sinh_6η0) - + 2 * (h2 * (dη0_dφ * cosh_4η0 * cos_4ξ0 - dξ0_dφ * sin_4ξ0 * sinh_4η0) - + 2 * h4 * (dη0_dφ * cosh_8η0 * cos_8ξ0 - dξ0_dφ * sin_8ξ0 * sinh_8η0))); + + 2 * (cf2 * (dη0_dφ * cosh_2η0 * cos_2ξ0 - dξ0_dφ * sin_2ξ0 * sinh_2η0) + + 3 * cf6 * (dη0_dφ * cosh_6η0 * cos_6ξ0 - dξ0_dφ * sin_6ξ0 * sinh_6η0) + + 2 * (cf4 * (dη0_dφ * cosh_4η0 * cos_4ξ0 - dξ0_dφ * sin_4ξ0 * sinh_4η0) + + 2 * cf8 * (dη0_dφ * cosh_8η0 * cos_8ξ0 - dξ0_dφ * sin_8ξ0 * sinh_8η0))); return new Matrix2(dη_dλ, dη_dφ, dξ_dλ, dξ_dφ); @@ -464,20 +480,22 @@ public class TransverseMercator extends final double[] dstPts, final int dstOff) throws ProjectionException { - final double η = srcPts[srcOff ]; - final double ξ = srcPts[srcOff + 1]; + final double η = srcPts[srcOff ]; + final double ξ = srcPts[srcOff+1]; /* * Following calculation of sin_2ξ, sin_4ξ, etc. is basically a copy-and-paste of the code in transform(…). * Its purpose is the same than for transform(…): reduce the amount of calls to Math.sin(double) and other * methods. */ - final double sin_2ξ, sin_4ξ, sin_6ξ, sin_8ξ, - cos_2ξ, cos_4ξ, cos_6ξ, cos_8ξ, - sinh_2η, sinh_4η, sinh_6η, sinh_8η, - cosh_2η, cosh_4η, cosh_6η, cosh_8η; - if (ORIGINAL_FORMULA) { - sin_2ξ = sin(2*ξ); - cos_2ξ = cos(2*ξ); + final double sin_2ξ = sin (2*ξ); + final double cos_2ξ = cos (2*ξ); + final double sinh_2η = sinh(2*η); + final double cosh_2η = cosh(2*η); + final double sin_4ξ, sin_6ξ, sin_8ξ, + cos_4ξ, cos_6ξ, cos_8ξ, + sinh_4η, sinh_6η, sinh_8η, + cosh_4η, cosh_6η, cosh_8η; + if (!ALLOW_TRIGONOMETRIC_IDENTITIES) { sin_4ξ = sin(4*ξ); cos_4ξ = cos(4*ξ); sin_6ξ = sin(6*ξ); @@ -485,8 +503,6 @@ public class TransverseMercator extends sin_8ξ = sin(8*ξ); cos_8ξ = cos(8*ξ); - sinh_2η = sinh(2*η); - cosh_2η = cosh(2*η); sinh_4η = sinh(4*η); cosh_4η = cosh(4*η); sinh_6η = sinh(6*η); @@ -494,40 +510,36 @@ public class TransverseMercator extends sinh_8η = sinh(8*η); cosh_8η = cosh(8*η); } else { - sin_2ξ = sin(2*ξ); - cos_2ξ = cos(2*ξ); final double sin2 = sin_2ξ * sin_2ξ; final double cos2 = cos_2ξ * cos_2ξ; - sin_4ξ = 2 * sin_2ξ * cos_2ξ; - cos_4ξ = cos2 - sin2; - sin_6ξ = (3 - 4*sin2) * sin_2ξ; - cos_6ξ = (4*cos2 - 3) * cos_2ξ; - sin_8ξ = 4*cos_4ξ * (sin_2ξ * cos_2ξ); - cos_8ξ = 1 - 2*sin_4ξ*sin_4ξ; + sin_4ξ = sin_2ξ * cos_2ξ; assert identityEquals(sin_4ξ, sin(4*ξ) / 2) : ξ; + cos_4ξ = (cos2 - sin2) * 0.5; assert identityEquals(cos_4ξ, cos(4*ξ) / 2) : ξ; + sin_6ξ = (0.75 - sin2) * sin_2ξ; assert identityEquals(sin_6ξ, sin(6*ξ) / 4) : ξ; + cos_6ξ = (cos2 - 0.75) * cos_2ξ; assert identityEquals(cos_6ξ, cos(6*ξ) / 4) : ξ; + sin_8ξ = sin_4ξ * cos_4ξ; assert identityEquals(sin_8ξ, sin(8*ξ) / 8) : ξ; + cos_8ξ = 0.125 - sin_4ξ * sin_4ξ; assert identityEquals(cos_8ξ, cos(8*ξ) / 8) : ξ; - sinh_2η = sinh(2*η); - cosh_2η = cosh(2*η); final double sinh2 = sinh_2η * sinh_2η; final double cosh2 = cosh_2η * cosh_2η; - sinh_4η = 2 * sinh_2η * cosh_2η; - cosh_4η = cosh2 + sinh2; - sinh_6η = (3 + 4*sinh2) * sinh_2η; - cosh_6η = (4*cosh2 - 3) * cosh_2η; - sinh_8η = 4*cosh_4η * (sinh_2η * cosh_2η); - cosh_8η = 1 + 2*sinh_4η*sinh_4η; + cosh_4η = (cosh2 + sinh2) * 0.5; assert identityEquals(cosh_4η, cosh(4*η) / 2) : η; + sinh_4η = cosh_2η * sinh_2η; assert identityEquals(sinh_4η, sinh(4*η) / 2) : η; + cosh_6η = cosh_2η * (cosh2 - 0.75); assert identityEquals(cosh_6η, cosh(6*η) / 4) : η; + sinh_6η = sinh_2η * (sinh2 + 0.75); assert identityEquals(sinh_6η, sinh(6*η) / 4) : η; + cosh_8η = sinh_4η * sinh_4η + 0.125; assert identityEquals(cosh_8η, cosh(8*η) / 8) : η; + sinh_8η = sinh_4η * cosh_4η; assert identityEquals(sinh_8η, sinh(8*η) / 8) : η; } /* * The actual inverse transform. */ - final double ξ0 = ξ - (ih4 * sin_8ξ * cosh_8η - + ih3 * sin_6ξ * cosh_6η - + ih2 * sin_4ξ * cosh_4η - + ih1 * sin_2ξ * cosh_2η); - - final double η0 = η - (ih4 * cos_8ξ * sinh_8η - + ih3 * cos_6ξ * sinh_6η - + ih2 * cos_4ξ * sinh_4η - + ih1 * cos_2ξ * sinh_2η); + final double ξ0 = ξ - (ci8 * sin_8ξ * cosh_8η + + ci6 * sin_6ξ * cosh_6η + + ci4 * sin_4ξ * cosh_4η + + ci2 * sin_2ξ * cosh_2η); + + final double η0 = η - (ci8 * cos_8ξ * sinh_8η + + ci6 * cos_6ξ * sinh_6η + + ci4 * cos_4ξ * sinh_4η + + ci2 * cos_2ξ * sinh_2η); final double β = asin(sin(ξ0) / cosh(η0)); final double Q = asinh(tan(β)); @@ -539,8 +551,8 @@ public class TransverseMercator extends final double c = excentricity * atanh(excentricity * tanh(Qp)); Qp = Q + c; if (abs(c - p) <= ITERATION_TOLERANCE) { - dstPts[dstOff ] = asin(tanh(η0) / cos(β)); - dstPts[dstOff + 1] = atan(sinh(Qp)); + dstPts[dstOff ] = asin(tanh(η0) / cos(β)); + dstPts[dstOff+1] = atan(sinh(Qp)); return; } p = c; @@ -582,8 +594,8 @@ public class TransverseMercator extends final double[] dstPts, final int dstOff, final boolean derivate) throws ProjectionException { - final double λ = srcPts[srcOff]; - final double φ = srcPts[srcOff + 1]; + final double λ = srcPts[srcOff ]; + final double φ = srcPts[srcOff+1]; final double sinλ = sin(λ); final double cosλ = cos(λ); final double sinφ = sin(φ); Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -37,7 +37,7 @@ import org.junit.Test; import static java.lang.StrictMath.*; import static java.lang.Double.*; -import static org.junit.Assert.*; +import static org.apache.sis.test.Assert.*; // Branch-specific imports import static org.junit.Assume.assumeTrue; @@ -53,7 +53,7 @@ import static org.apache.sis.test.Assert * @author Martin Desruisseaux (Geomatys) * @author Rémi Maréchal (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ @DependsOn(ConformalProjectionTest.class) @@ -340,4 +340,23 @@ public final strictfp class LambertConic tolerance = Formulas.LINEAR_TOLERANCE; compareEllipticalWithSpherical(CoordinateDomain.GEOGRAPHIC_SAFE, 0); } + + /** + * Verifies that deserialized projections work as expected. This implies that deserialization + * recomputed the internal transient fields, especially the series expansion coefficients. + * + * @throws FactoryException if an error occurred while creating the map projection. + * @throws TransformException if an error occurred while projecting a coordinate. + */ + @Test + @DependsOnMethod("testLambertConicConformal1SP") + public void testSerialization() throws FactoryException, TransformException { + createNormalizedProjection(true, 40); + final double[] source = CoordinateDomain.GEOGRAPHIC_RADIANS_NORTH.generateRandomInput(TestUtilities.createRandomNumberGenerator(), 2, 10); + final double[] target = new double[source.length]; + transform.transform(source, 0, target, 0, 10); + transform = assertSerializedEquals(transform); + tolerance = Formulas.LINEAR_TOLERANCE; + verifyTransform(source, target); + } } Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -75,6 +75,7 @@ final strictfp class NoOp extends Confor super(new Initializer(new DefaultOperationMethod( Collections.singletonMap(DefaultOperationMethod.NAME_KEY, parameters.getDescriptor().getName()), 2, 2, parameters.getDescriptor()), parameters, Collections.>emptyMap(), (byte) 0)); + super.computeCoefficients(); } /** Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -24,9 +24,11 @@ import org.apache.sis.parameter.Paramete import org.apache.sis.referencing.operation.transform.CoordinateDomain; import org.apache.sis.test.DependsOnMethod; import org.apache.sis.test.DependsOn; +import org.apache.sis.test.TestUtilities; import org.junit.Test; import static java.lang.StrictMath.toRadians; +import static org.apache.sis.test.Assert.*; /** @@ -34,7 +36,7 @@ import static java.lang.StrictMath.toRad * * @author Martin Desruisseaux (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ @DependsOn(NormalizedProjectionTest.class) @@ -138,4 +140,23 @@ public final strictfp class TransverseMe verifyDerivative(toRadians(-3), toRadians(30)); verifyDerivative(toRadians(+6), toRadians(60)); } + + /** + * Verifies that deserialized projections work as expected. This implies that deserialization + * recomputed the internal transient fields, especially the series expansion coefficients. + * + * @throws FactoryException if an error occurred while creating the map projection. + * @throws TransformException if an error occurred while projecting a coordinate. + */ + @Test + @DependsOnMethod("testTransverseMercator") + public void testSerialization() throws FactoryException, TransformException { + createNormalizedProjection(true, 40); + final double[] source = CoordinateDomain.GEOGRAPHIC_RADIANS_HALF_λ.generateRandomInput(TestUtilities.createRandomNumberGenerator(), 2, 10); + final double[] target = new double[source.length]; + transform.transform(source, 0, target, 0, 10); + transform = assertSerializedEquals(transform); + tolerance = Formulas.LINEAR_TOLERANCE; + verifyTransform(source, target); + } } Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -16,8 +16,10 @@ */ package org.apache.sis.internal.jaxb; +import java.net.URI; import java.util.Set; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.Collection; import java.util.Collections; @@ -28,7 +30,7 @@ import java.io.Serializable; import org.opengis.metadata.Identifier; import org.opengis.metadata.citation.Citation; import org.apache.sis.util.Debug; -import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.xml.XLink; import org.apache.sis.xml.IdentifierMap; import org.apache.sis.xml.IdentifierSpace; @@ -39,14 +41,21 @@ import org.apache.sis.internal.jdk7.Obje /** - * A map of identifiers which can be used as a helper class for - * {@link org.apache.sis.xml.IdentifiedObject} implementations. + * Implementation of the map of identifiers associated to {@link org.apache.sis.xml.IdentifiedObject} instances. + * This base class implements an unmodifiable map, but the {@link ModifiableIdentifierMap} subclass add write + * capabilities. * *

This class works as a wrapper around a collection of identifiers. Because all operations * are performed by an iteration over the collection elements, this implementation is suitable * only for small maps (less than 10 elements). Given that objects typically have only one or * two identifiers, this is considered acceptable.

* + *
Special cases
+ * The identifiers for the following authorities are handled in a special way: + *
    + *
  • {@link IdentifierSpace#HREF}: handled as a shortcut to {@link XLink#getHRef()}.
  • + *
+ * *
Handling of duplicated authorities
* The collection shall not contain more than one identifier for the same * {@linkplain Identifier#getAuthority() authority}. However duplications may happen if the user @@ -65,22 +74,17 @@ import org.apache.sis.internal.jdk7.Obje * * *
Handling of null identifiers
- * The collection of identifiers shall not contains any null element. This is normally ensured by + * The collection of identifiers shall not contain any null element. This is normally ensured by * the {@link org.apache.sis.metadata.ModifiableMetadata} internal collection implementations. - * This class performs opportunist null checks as an additional safety. However because we perform - * those checks only in opportunist ways, the following inconsistencies remain: - * - *
    - *
  • {@link #isEmpty()} may return {@code false} when the more accurate {@link #size()} - * method returns 0.
  • - *
+ * This class performs opportunist null checks as an additional safety, but consistency is not + * guaranteed. See {@link #size()} for more information. * *
Thread safety
* This class is thread safe if the underlying identifier collection is thread safe. * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module * * @see org.apache.sis.xml.IdentifiedObject @@ -106,46 +110,113 @@ public class IdentifierMapAdapter extend * * @param identifiers The identifiers to wrap in a map view. */ - IdentifierMapAdapter(final Collection identifiers) { + public IdentifierMapAdapter(final Collection identifiers) { this.identifiers = identifiers; } /** - * Removes every entries in the underlying collection. + * If the given authority is a special case, returns its {@link NonMarshalledAuthority} integer enum. + * Otherwise returns -1. See javadoc for more information about special cases. * - * @throws UnsupportedOperationException If the collection of identifiers is unmodifiable. + * @param authority A {@link Citation} constant. The type is relaxed to {@code Object} + * because the signature of some {@code Map} methods are that way. */ - @Override - public void clear() throws UnsupportedOperationException { - identifiers.clear(); + static int specialCase(final Object authority) { + if (authority == IdentifierSpace.HREF) return NonMarshalledAuthority.HREF; + // A future Apache SIS version may add more special cases here. + return -1; + } + + /** + * Extracts the {@code xlink:href} value from the {@link XLink} if presents. + * This method does not test if an explicit {@code xlink:href} identifier exists; + * this check must be done by the caller before to invoke this method. + * + * @see ModifiableIdentifierMap#setHRef(URI) + */ + private URI getHRef() { + final Identifier identifier = getIdentifier(IdentifierSpace.XLINK); + if (identifier instanceof SpecializedIdentifier) { + final Object link = ((SpecializedIdentifier) identifier).value; + if (link instanceof XLink) { + return ((XLink) link).getHRef(); + } + } + return null; + } + + /** + * Returns the string representation of the given value, or {@code null} if none. + * + * @param value The value returned be one of the above {@code getFoo()} methods. + */ + private static String toString(final Object value) { + return (value != null) ? value.toString() : null; + } + + + + + //////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// END OF SPECIAL CASES. //////// + //////// //////// + //////// Implementation of IdentifierMap methods follow. Each method may //////// + //////// have a switch statement over the special cases declared above. //////// + //////// //////// + //////////////////////////////////////////////////////////////////////////////////////// + + /** + * Whether this map support {@code put} and {@code remove} operations. + */ + boolean isModifiable() { + return false; } /** * Returns {@code true} if the collection of identifiers contains at least one element. * This method does not verify if the collection contains null element (it should not). - * Consequently, this method may return {@code false} even if the {@link #size()} method - * returns 0. */ @Override - public boolean isEmpty() { + public final boolean isEmpty() { return identifiers.isEmpty(); } /** - * Returns {@code true} if at least one identifier declares the given - * {@linkplain Identifier#getCode() code}. + * Counts the number of entries, ignoring null elements and duplicated authorities. + * + *

Because {@code null} elements are ignored, this method may return 0 even if {@link #isEmpty()} + * returns {@code false}. However this inconsistency should not happen in practice because + * {@link org.apache.sis.metadata.ModifiableMetadata} internal collection implementations + * do not allow null values.

+ */ + @Override + public final int size() { + final HashSet done = new HashSet(hashMapCapacity(identifiers.size())); + for (final Identifier identifier : identifiers) { + if (identifier != null) { + done.add(identifier.getAuthority()); + } + } + return done.size(); + } + + /** + * Returns {@code true} if at least one identifier declares the given {@linkplain Identifier#getCode() code}. * * @param code The code to search, which should be an instance of {@link String}. * @return {@code true} if at least one identifier uses the given code. */ @Override - public boolean containsValue(final Object code) { + public final boolean containsValue(final Object code) { if (code instanceof String) { for (final Identifier identifier : identifiers) { if (identifier != null && code.equals(identifier.getCode())) { return true; } } + return code.equals(toString(getHRef())); + // A future Apache SIS version may add more special cases here. } return false; } @@ -158,14 +229,23 @@ public class IdentifierMapAdapter extend * @return {@code true} if at least one identifier uses the given authority. */ @Override - public boolean containsKey(final Object authority) { - return (authority instanceof Citation) && getIdentifier((Citation) authority) != null; + public final boolean containsKey(final Object authority) { + if (authority instanceof Citation) { + if (getIdentifier((Citation) authority) != null) { + return true; + } + switch (specialCase(authority)) { + case NonMarshalledAuthority.HREF: return getHRef() != null; + // A future Apache SIS version may add more special cases here. + } + } + return false; } /** * Returns the identifier for the given key, or {@code null} if none. */ - private Identifier getIdentifier(final Citation authority) { + final Identifier getIdentifier(final Citation authority) { for (final Identifier identifier : identifiers) { if (identifier != null && Objects.equals(authority, identifier.getAuthority())) { return identifier; @@ -180,148 +260,94 @@ public class IdentifierMapAdapter extend */ @Override @SuppressWarnings("unchecked") - public T getSpecialized(final IdentifierSpace authority) { - final Identifier identifier = getIdentifier(authority); - return (identifier instanceof SpecializedIdentifier) ? ((SpecializedIdentifier) identifier).value : null; - } - - /** - * Returns the code of the first identifier associated with the given authority only if - * if is not a specialized identifier. Otherwise returns {@code null}. - * - *

This is a helper method for {@link IdentifierMapWithSpecialCases#put(Citation, String)}, - * in order to be able to return the old value if that value was a {@link String} rather than - * the specialized type. We do not return the string for the specialized case in order to avoid - * the cost of invoking {@code toString()} on the specialized object (some may be costly). Such - * call would be useless because {@code IdentifierMapWithSpecialCase} discard the value of this - * method when it found a specialized type.

- */ - final String getUnspecialized(final Citation authority) { + public final T getSpecialized(final IdentifierSpace authority) { final Identifier identifier = getIdentifier(authority); - if (identifier != null && !(identifier instanceof SpecializedIdentifier)) { - return identifier.getCode(); + if (identifier instanceof SpecializedIdentifier) { + return ((SpecializedIdentifier) identifier).value; + } + switch (specialCase(authority)) { + case NonMarshalledAuthority.HREF: return (T) getHRef(); + // A future Apache SIS version may add more special cases here. } return null; } /** * Returns the code of the first identifier associated with the given - * {@linkplain Identifier#getAuthority() authority}, or {@code null} - * if no identifier was found. + * {@linkplain Identifier#getAuthority() authority}, or {@code null} if no identifier was found. * * @param authority The authority to search, which should be an instance of {@link Citation}. * @return The code of the identifier for the given authority, or {@code null} if none. */ @Override - public String get(final Object authority) { + public final String get(final Object authority) { if (authority instanceof Citation) { final Identifier identifier = getIdentifier((Citation) authority); if (identifier != null) { return identifier.getCode(); } + switch (specialCase(authority)) { + case NonMarshalledAuthority.HREF: return toString(getHRef()); + // A future Apache SIS version may add more special cases here. + } } return null; } /** * Removes all identifiers associated with the given {@linkplain Identifier#getAuthority() authority}. - * The default implementation delegates to {@link #put(Citation, String)} with a {@code null} value. * * @param authority The authority to search, which should be an instance of {@link Citation}. * @return The code of the identifier for the given authority, or {@code null} if none. + * @throws UnsupportedOperationException if the collection of identifiers is unmodifiable. */ @Override - public String remove(final Object authority) { - return (authority instanceof Citation) ? put((Citation) authority, null) : null; + public String remove(Object authority) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Removes every entries in the underlying collection. + * + * @throws UnsupportedOperationException if the collection of identifiers is unmodifiable. + */ + @Override + public void clear() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); } /** * Sets the code of the identifier having the given authority to the given value. - * If no identifier is found for the given authority, a new one is created. If - * more than one identifier is found for the given authority, then all previous - * identifiers may be removed in order to ensure that the new entry will be the - * first entry, so it can be find by the {@code get} method. + * If no identifier is found for the given authority, a new one is created. + * If more than one identifier is found for the given authority, then all previous identifiers may be removed + * in order to ensure that the new entry will be the first entry, so it can be find by the {@code get} method. * * @param authority The authority for which to set the code. * @param code The new code for the given authority, or {@code null} for removing the entry. * @return The previous code for the given authority, or {@code null} if none. + * @throws UnsupportedOperationException if the collection of identifiers is unmodifiable. */ @Override - public String put(final Citation authority, final String code) - throws UnsupportedOperationException - { - ArgumentChecks.ensureNonNull("authority", authority); - String old = null; - final Iterator it = identifiers.iterator(); - while (it.hasNext()) { - final Identifier identifier = it.next(); - if (identifier == null) { - it.remove(); // Opportunist cleaning, but should not happen. - } else if (Objects.equals(authority, identifier.getAuthority())) { - if (code != null && identifier instanceof IdentifierMapEntry) { - return ((IdentifierMapEntry) identifier).setValue(code); - // No need to suppress other occurrences of the key (if any) - // because we made a replacement in the first entry, so the - // new value will be visible by the getter methods. - } - if (old == null) { - old = identifier.getCode(); - } - it.remove(); - // Continue the iteration in order to remove all other occurrences, - // in order to ensure that the getter methods will see the new value. - } - } - if (code != null) { - identifiers.add(SpecializedIdentifier.parse(authority, code)); - } - return old; + public String put(Citation authority, String code) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); } /** * Sets the identifier associated with the given authority, and returns the previous value. */ @Override - public T putSpecialized(final IdentifierSpace authority, final T value) - throws UnsupportedOperationException - { - ArgumentChecks.ensureNonNull("authority", authority); - T old = null; - final Iterator it = identifiers.iterator(); - while (it.hasNext()) { - final Identifier identifier = it.next(); - if (identifier == null) { - it.remove(); // Opportunist cleaning, but should not happen. - } else if (Objects.equals(authority, identifier.getAuthority())) { - if (identifier instanceof SpecializedIdentifier) { - @SuppressWarnings("unchecked") - final SpecializedIdentifier id = (SpecializedIdentifier) identifier; - if (old == null) { - old = id.value; - } - if (value != null) { - id.value = value; - return old; - // No need to suppress other occurrences of the key (if any) - // because we made a replacement in the first entry, so the - // new value will be visible by the getter methods. - } - } - it.remove(); - // Continue the iteration in order to remove all other occurrences, - // in order to ensure that the getter methods will see the new value. - } - } - if (value != null) { - identifiers.add(new SpecializedIdentifier(authority, value)); - } - return old; + public T putSpecialized(IdentifierSpace authority, T value) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); } /** * Returns a view over the collection of identifiers. This view supports removal operation * if the underlying collection of identifiers supports the {@link Iterator#remove()} method. * + *

If the backing identifier collection contains null entries, those entries will be ignored. + * If the backing collection contains many entries for the same authority, then only the first + * occurrence is included.

+ * * @return A view over the collection of identifiers. */ @Override @@ -332,83 +358,35 @@ public class IdentifierMapAdapter extend * fields if the underlying list is thread-safe. Furthermore, IdentifierMapAdapter are temporary * objects anyway in the current ISOMetadata implementation. */ - return new Entries(identifiers); - } - - /** - * The view returned by {@link IdentifierMapAdapter#entrySet()}. If the backing identifier collection - * contains null entries, those entries will be ignored. If the backing collection contains many entries - * for the same authority, then only the first occurrence is retained. - * - * @author Martin Desruisseaux (Geomatys) - * @since 0.3 - * @version 0.3 - * @module - */ - private static final class Entries extends AbstractSet> { - /** - * The identifiers to wrap in a set of entries view. This is a reference - * to the same collection than {@link IdentifierMapAdapter#identifiers}. - */ - private final Collection identifiers; - - /** - * Creates a new view over the collection of identifiers. - * - * @param identifiers The identifiers to wrap in a set of entries view. - */ - Entries(final Collection identifiers) { - this.identifiers = identifiers; - } - - /** - * Same implementation than {@link IdentifierMapAdapter#clear()}. - */ - @Override - public void clear() throws UnsupportedOperationException { - identifiers.clear(); - } + return new AbstractSet>() { + /** Delegates to the enclosing class. */ + @Override public void clear() throws UnsupportedOperationException { + IdentifierMapAdapter.this.clear(); + } - /** - * Same implementation than {@link IdentifierMapAdapter#isEmpty()}. - */ - @Override - public boolean isEmpty() { - return identifiers.isEmpty(); - } + /** Delegates to the enclosing class. */ + @Override public boolean isEmpty() { + return IdentifierMapAdapter.this.isEmpty(); + } - /** - * Counts the number of entries, ignoring null elements and duplicated authorities. - * Because {@code null} elements are ignored, this method may return 0 even if - * {@link #isEmpty()} returns {@code false}. - */ - @Override - public int size() { - final HashMap done = new HashMap(hashMapCapacity(identifiers.size())); - for (final Identifier identifier : identifiers) { - if (identifier != null) { - done.put(identifier.getAuthority(), null); - } + /** Delegates to the enclosing class. */ + @Override public int size() { + return IdentifierMapAdapter.this.size(); } - return done.size(); - } - /** - * Returns an iterator over the (citation, code) entries. - */ - @Override - public Iterator> iterator() { - return new Iter(identifiers); - } + /** Returns an iterator over the (citation, code) entries. */ + @Override public Iterator> iterator() { + return new Iter(identifiers, isModifiable()); + } + }; } /** * The iterator over the (citation, code) entries. This iterator is created by * the {@link IdentifierMapAdapter.Entries} collection. It extends {@link HashMap} as an opportunist - * implementation strategy, but users don't need to know this detail. + * implementation strategy, but users does not need to know this detail. * - *

This iterator supports the {@link #remove()} operation if the underlying collection - * supports it.

+ *

This iterator supports the {@link #remove()} operation if the underlying collection supports it.

* *

The map entries are used as a safety against duplicated authority values. The map values * are non-null only after we iterated over an authority. Then the value is {@link Boolean#TRUE} @@ -416,7 +394,7 @@ public class IdentifierMapAdapter extend * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module */ @SuppressWarnings("serial") // Not intended to be serialized. @@ -439,11 +417,17 @@ public class IdentifierMapAdapter extend private transient Citation authority; /** + * {@code true} if the iterator should support the {@link #remove()} operation. + */ + private final boolean isModifiable; + + /** * Creates a new iterator for the given collection of identifiers. */ - Iter(final Collection identifiers) { + Iter(final Collection identifiers, final boolean isModifiable) { super(hashMapCapacity(identifiers.size())); this.identifiers = identifiers.iterator(); + this.isModifiable = isModifiable; } /** @@ -518,6 +502,9 @@ public class IdentifierMapAdapter extend */ @Override public void remove() throws IllegalStateException { + if (!isModifiable) { + throw new UnsupportedOperationException(); + } final Iterator it = identifiers; if (it == null || next != null) { throw new IllegalStateException(); Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -17,12 +17,18 @@ package org.apache.sis.internal.jaxb; import java.util.Iterator; +import java.util.List; import java.util.ArrayList; +import java.util.Map; +import java.util.IdentityHashMap; import java.util.Collection; +import java.util.Collections; import org.opengis.metadata.Identifier; import org.opengis.metadata.citation.Citation; import org.apache.sis.internal.simple.CitationConstant; +import org.apache.sis.internal.util.CollectionsExt; import org.apache.sis.internal.util.UnmodifiableArrayList; +import org.apache.sis.util.collection.Containers; import org.apache.sis.xml.IdentifierSpace; @@ -57,7 +63,7 @@ import org.apache.sis.xml.IdentifierSpac * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.6 + * @version 0.7 * @module * * @see IdentifierSpace @@ -73,6 +79,7 @@ public final class NonMarshalledAuthorit * mirror the constants defined in the {@link IdentifierSpace} interface * and {@link org.apache.sis.metadata.iso.citation.DefaultCitation} class. */ + @SuppressWarnings("FieldNameHidesFieldInSuperclass") public static final byte ID=0, UUID=1, HREF=2, XLINK=3, ISSN=4, ISBN=5; // If more codes are added, please update readResolve() below. @@ -103,6 +110,9 @@ public final class NonMarshalledAuthorit * "special" identifiers (ISO 19139 attributes, ISBN codes...), which are recognized by * the implementation class of their authority. * + *

This method is used for implementation of {@code getIdentifier()} methods (singular form) + * in public metadata objects.

+ * * @param The type of object used as identifier values. * @param identifiers The collection from which to get identifiers, or {@code null}. * @return The first identifier, or {@code null} if none. @@ -124,23 +134,26 @@ public final class NonMarshalledAuthorit * method is used when the given collection is expected to contains only one ISO 19115 * identifier. * - * @param The type of object used as identifier values. + *

This method is used for implementation of {@code setIdentifier(Identifier)} methods + * in public metadata objects.

+ * + * @param The type of object used as identifier values. * @param identifiers The collection in which to add the identifier. - * @param id The identifier to add, or {@code null}. + * @param newValue The identifier to add, or {@code null}. + * + * @see #setMarshallables(Collection, Collection) */ - public static void setMarshallable(final Collection identifiers, final T id) { + public static void setMarshallable(final Collection identifiers, final T newValue) { final Iterator it = identifiers.iterator(); while (it.hasNext()) { final T old = it.next(); - if (old != null) { - if (old.getAuthority() instanceof NonMarshalledAuthority) { - continue; // Don't touch this identifier. - } + if (old != null && old.getAuthority() instanceof NonMarshalledAuthority) { + continue; // Don't touch this identifier. } it.remove(); } - if (id != null) { - identifiers.add(id); + if (newValue != null) { + identifiers.add(newValue); } } @@ -149,10 +162,15 @@ public final class NonMarshalledAuthorit * for which the authority is an instance of {@code NonMarshalledAuthority}. This should exclude * all {@link org.apache.sis.xml.IdentifierSpace} constants. * + *

This method is used for implementation of {@code getIdentifiers()} methods (plural form) + * in public metadata objects. Note that those methods override + * {@link org.apache.sis.xml.IdentifiedObject#getIdentifiers()}, which is expected to return + * all identifiers in normal (non-marshalling) usage.

+ * * @param identifiers The identifiers to filter, or {@code null}. * @return The identifiers to marshal, or {@code null} if none. */ - public static Collection excludeOnMarshalling(Collection identifiers) { + public static Collection filterOnMarshalling(Collection identifiers) { if (identifiers != null && Context.isFlagSet(Context.current(), Context.MARSHALLING)) { int count = identifiers.size(); if (count != 0) { @@ -170,69 +188,72 @@ public final class NonMarshalledAuthorit } /** - * Returns a collection containing only the identifiers having a {@code NonMarshalledAuthority}. - * This method is invoked for saving the identifiers that are conceptually stored in distinct fields - * (XML identifier, UUID, ISBN, ISSN) before to overwrite the collection of all identifiers in - * a metadata object. - * - *

This method is invoked from {@code setIdentifiers(Collection)} implementation - * in {@link org.apache.sis.metadata.iso.ISOMetadata} subclasses as below:

- * - * {@preformat java - * final Collection oldIds = NonMarshalledAuthority.filteredCopy(identifiers); - * identifiers = writeCollection(newValues, identifiers, Identifier.class); - * NonMarshalledAuthority.replace(identifiers, oldIds); - * } + * Returns a collection containing all marshallable values of {@code newValues}, together with unmarshallable + * values of {@code identifiers}. This method is invoked for preserving the identifiers that are conceptually + * stored in distinct fields (XML identifier, UUID, ISBN, ISSN) when setting the collection of all identifiers + * in a metadata object. + * + *

This method is used for implementation of {@code setIdentifiers(Collection)} methods + * in public metadata objects.

* - * @param The type of object used as identifier values. * @param identifiers The metadata internal identifiers collection, or {@code null} if none. - * @return The new list containing the filtered identifiers, or {@code null} if none. + * @param newValues The identifiers to add, or {@code null}. + * @return The collection to set (may be {@code newValues}. + * + * @see #setMarshallable(Collection, Identifier) */ - public static Collection filteredCopy(final Collection identifiers) { - Collection filtered = null; - if (identifiers != null) { - int remaining = identifiers.size(); - for (final T candidate : identifiers) { - if (candidate != null && candidate.getAuthority() instanceof NonMarshalledAuthority) { - if (filtered == null) { - filtered = new ArrayList(remaining); - } - filtered.add(candidate); + @SuppressWarnings("null") + public static Collection setMarshallables( + final Collection identifiers, final Collection newValues) + { + int remaining; + if (identifiers == null || (remaining = identifiers.size()) == 0) { + return newValues; + } + /* + * If there is any identifiers that need to be preserved (XML identifier, UUID, ISBN, etc.), + * remember them. Otherwise there is nothing special to do and we can return the new values directly. + */ + List toPreserve = null; + for (final Identifier id : identifiers) { + if (id != null && id.getAuthority() instanceof NonMarshalledAuthority) { + if (toPreserve == null) { + toPreserve = new ArrayList(remaining); } - remaining--; + toPreserve.add(id); } + remaining--; } - return filtered; - } - - /** - * Replaces all identifiers in the {@code identifiers} collection having the same - * {@linkplain Identifier#getAuthority() authority} than the ones in {@code oldIds}. - * More specifically: - * - *
    - *
  • First, remove all {@code identifiers} elements having the same authority - * than one of the elements in {@code oldIds}.
  • - *
  • Next, add all {@code oldIds} elements to {@code identifiers}.
  • - *
- * - * @param The type of object used as identifier values. - * @param identifiers The metadata internal identifiers collection, or {@code null} if none. - * @param oldIds The previous filtered identifiers returned by {@link #filteredCopy(Collection)}, - * or {@code null} if none. - */ - public static void replace(final Collection identifiers, final Collection oldIds) { - if (oldIds != null && identifiers != null) { - for (final T old : oldIds) { - final Citation authority = old.getAuthority(); - for (final Iterator it=identifiers.iterator(); it.hasNext();) { - final T id = it.next(); - if (id == null || id.getAuthority() == authority) { - it.remove(); - } + if (toPreserve == null) { + return newValues; + } + /* + * We find at least one identifier that may need to be preserved. + * We need to create a combination of the two collections. + */ + final Map authorities = new IdentityHashMap(4); + final List merged = new ArrayList(newValues.size()); + for (final Identifier id : newValues) { + merged.add(id); + if (id != null) { + final Citation authority = id.getAuthority(); + if (authority instanceof NonMarshalledAuthority) { + authorities.put(authority, id); } } - identifiers.addAll(oldIds); + } + for (final Identifier id : toPreserve) { + if (!authorities.containsKey(id.getAuthority())) { + merged.add(id); + } + } + /* + * Wraps in an unmodifiable list in case the caller is creating an unmodifiable metadata. + */ + switch (merged.size()) { + case 0: return Collections.emptyList(); + case 1: return Collections.singletonList(merged.get(0)); + default: return Containers.unmodifiableList(CollectionsExt.toArray(merged, Identifier.class)); } } Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -44,10 +44,10 @@ import org.apache.sis.internal.jdk7.Obje * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module */ -public final class SpecializedIdentifier implements Identifier, Serializable { +public final class SpecializedIdentifier implements Identifier, Cloneable, Serializable { /** * For cross-version compatibility. */ @@ -210,6 +210,20 @@ public final class SpecializedIdentifier } /** + * Returns a clone of this identifier. + * + * @return A shallow clone of this identifier. + */ + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); // Should never happen, since we are cloneable. + } + } + + /** * Returns a string representation of this identifier. * Example: {@code Identifier[gco:uuid=“42924124-032a-4dfe-b06e-113e3cb81cf0”]}. * Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -206,7 +206,7 @@ public abstract class PropertyType * * We do not try to parse UUID or XLink objects from String because it should be the job of - * org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases.put(Citation, String). + * org.apache.sis.internal.jaxb.ModifiableIdentifierMap.put(Citation, String). */ final IdentifierMap map = ((IdentifiedObject) value).getIdentifierMap(); XLink link = map.getSpecialized(IdentifierSpace.XLINK); Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -26,7 +26,7 @@ *
Main content
* {@link org.apache.sis.internal.jaxb.IdentifierMapAdapter} is our internal implementation of * the public {@link org.apache.sis.xml.IdentifierMap} interface. The actual implementation is - * usually the {@code IdentifierMapWithSpecialCases} subclass. + * usually the {@code ModifiableIdentifierMap} subclass. * *

{@link org.apache.sis.internal.jaxb.SpecializedIdentifier} wraps {@link org.apache.sis.xml.XLink}, * {@link java.net.URI} and {@link java.util.UUID} as {@link org.opengis.metadata.Identifier} instances. Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -547,7 +547,6 @@ public class CodeListSet clone() { - @SuppressWarnings("unchecked") final CodeListSet clone; try { clone = (CodeListSet) super.clone(); Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -89,8 +89,7 @@ public final class Containers extends St * * @param The base type of elements in the list. * @param array The array to wrap, or {@code null} if none. - * @return The given array wrapped in an unmodifiable list, or {@code null} if the given - * array was null. + * @return The given array wrapped in an unmodifiable list, or {@code null} if the given array was null. * * @see java.util.Arrays#asList(Object[]) */ @@ -148,10 +147,9 @@ public final class Containers extends St * @param The type of elements in the storage (original) set. * @param The type of elements in the derived set. * @param storage The storage set containing the original elements, or {@code null}. - * @param converter The converter from the elements in the storage set to the elements - * in the derived set. - * @return A view over the {@code storage} set containing all elements converted by the given - * converter, or {@code null} if {@code storage} was null. + * @param converter The converter from the elements in the storage set to the elements in the derived set. + * @return A view over the {@code storage} set containing all elements converted by the given converter, + * or {@code null} if {@code storage} was null. * * @see org.apache.sis.util.ObjectConverters#derivedSet(Set, ObjectConverter) */ @@ -195,8 +193,8 @@ public final class Containers extends St * @param storage The storage map containing the original entries, or {@code null}. * @param keyConverter The converter from the keys in the storage map to the keys in the derived map. * @param valueConverter The converter from the values in the storage map to the values in the derived map. - * @return A view over the {@code storage} map containing all entries converted by the given - * converters, or {@code null} if {@code storage} was null. + * @return A view over the {@code storage} map containing all entries converted by the given converters, + * or {@code null} if {@code storage} was null. * * @see org.apache.sis.util.ObjectConverters#derivedMap(Map, ObjectConverter, ObjectConverter) * @see org.apache.sis.util.ObjectConverters#derivedKeys(Map, ObjectConverter, Class) Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -66,6 +66,5 @@ public interface IdentifierMap extends M * if there was no mapping of the specialized type for {@code authority}. * @throws UnsupportedOperationException If the identifier map is unmodifiable. */ - T putSpecialized(IdentifierSpace authority, T value) - throws UnsupportedOperationException; + T putSpecialized(IdentifierSpace authority, T value) throws UnsupportedOperationException; } Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java?rev=1707309&r1=1707308&r2=1707309&view=diff ============================================================================== --- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java [UTF-8] (original) +++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java [UTF-8] Wed Oct 7 13:59:47 2015 @@ -31,7 +31,7 @@ import org.apache.sis.util.ComparisonMod import org.apache.sis.util.LenientComparable; import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.jaxb.IdentifierMapAdapter; -import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases; +import org.apache.sis.internal.jaxb.ModifiableIdentifierMap; // Branch-dependent imports import org.apache.sis.internal.jdk7.Objects; @@ -73,7 +73,7 @@ final class NilObjectHandler implements asList.add(identifier); } } - attribute = new IdentifierMapWithSpecialCases(asList); + attribute = new ModifiableIdentifierMap(asList); } /**