JDK-6404847 : REGRESSION: GeneralPath.createTransformedShape no longer returns a GeneralPath object
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2006-03-28
  • Updated: 2012-03-23
  • Resolved: 2006-05-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
6 b83Fixed
Related Reports
Relates :  
GeneralPath has a createTransformedShape method which is declared to return
a Shape.  Before Mustang, this method always returned a GeneralPath object.
In Mustang, the Path2D classes were created to offer alternate storage
precision for paths and as a result now this method can (and does) return
a double precision path which provides for more accuracy.  Unfortunately,
this means the returned object is no longer the same type as GeneralPath.

While returning a double precision path from this method represents the best
tradeoff in terms of precision, the new return type will not be compatible
with code that may have expected the method to return a GeneralPath object.
As it turns out, as an example of existing code that may be broken by this
change in behavior, assumptions were discovered in some of the JCK tests that
this method would return a GeneralPath object even though it was not declared
to do so.  These tests then failed.  From a purely implementational point
of view, those tests are broken since they make an assumption that goes beyond
the spec.  But from a practical point of view, they may be an indicator that
other code may exist which might make this same erroneous assumption.

While the new behavior is definitely valid, the change has potential to break
existing code so we should examine that possibility and decide if we want to
maintain strict behavioral compatibility or provide better precision.

EVALUATION To avoid any unnecessary applications breaking in the field, the Path2D.createTransformedShape method will return an object of the same class as the original (via clone()). This means that the storage requirements for the transformed shape will remain the same as the original object as well so we avoid "storage bloat" surprises at the same time. If developers want to have better precision (or lower storage size) they can use the Path2D.Double (or Path2D.Float) constructors that take a Shape and a transform and explicitly control what kind of storage is used.

WORK AROUND Code which does not cast the return value of this method to anything other than Shape will not be broken so no workaround is needed. The rest of this entry will discuss how to fix code that does cast the return value to a GeneralPath type. Code which does not need to manipulate the return value of this method using any Generalpath-specific methods can be changed to simply not cast the return value and use Shape as the variable type to store the value. Such code will continue to work on both old and new runtimes. Code which needs to access GeneralPath-specific methods on the return value could correctly enforce its expectations by instead using the following code snippet: GeneralPath copy = new GeneralPath(origpath); copy.transform(at); This will result in a variable of type "GeneralPath" which contains the transformed geometry and will continue to work on both old and new runtimes. Code which will not be expected to run on pre-Mustang runtimes can use either of the 2 new Path2D constructors to both control the storage precision and type of the result: Path2D floatcopy = new Path2D.Float(origpath, at); Path2D doublecopy = new Path2D.Double(origpath, at); // The earlier workaround still works for GeneralPath: GeneralPath gpcopy = new GeneralPath(origpath); gpcopy.transform(at);

SUGGESTED FIX Have each of the 3 Path2D sub-types (Float, Double, GeneralPath) implement the method specifically to return a Shape of the same type as the original. Optionally, Path2D.Float could return Path2D.Double since it has no code that depends on its return type yet, but GeneralPath minimally would need to override the result and return the appropriate type. Note that since we want these methods to be final to protect the behavior of the classes in the presence of subclassing, and since GeneralPath subclasses the instantiable Path2D.Float class, we would want to make the method be final no lower than Path2D.Float so the Float version of the method would have to special case its implementation on GeneralPath's behalf. Also note that since Mustang provides convenience constructors on the 2 Path2D subclasses (Float and Double) to construct a transformed path from a source path or shape, there is already a mechanism for the developer to control the precision and storage requirements of their transformed geometry. As a result the requirement that the createTransformedShape method satisfy everyone's needs on both fronts is no longer an issue.