This is an automated email from the ASF dual-hosted git repository. dkulp pushed a commit to branch datatypes in repository https://gitbox.apache.org/repos/asf/ws-axiom.git
commit d63b90cdbe9f6955401712b1ea6f3b15babc584a Author: Andreas Veithen <veit...@apache.org> AuthorDate: Sun Nov 15 19:30:57 2015 +0000 Initial datatype library code. --- aspects/core-aspects/pom.xml | 5 + .../org/apache/axiom/core/ContextAccessorImpl.java | 18 +- .../java/org/apache/axiom/core/CoreElement.java | 5 + .../org/apache/axiom/core/CoreElementSupport.aj | 17 ++ aspects/fom-aspects/pom.xml | 5 + .../org/apache/axiom/fom/AbderaDateTimeMixin.aj | 18 +- {aspects/core-aspects => datatypes}/pom.xml | 35 ++- .../axiom/datatype/AbstractInvariantType.java | 14 +- .../org/apache/axiom/datatype/ContextAccessor.java | 7 +- .../apache/axiom/datatype/DOMContextAccessor.java | 21 +- .../java/org/apache/axiom/datatype/DOMHelper.java | 19 +- .../org/apache/axiom/datatype/InvariantType.java | 9 +- .../main/java/org/apache/axiom/datatype/Type.java | 9 +- .../datatype/UnexpectedCharacterException.java | 12 +- .../datatype/UnexpectedEndOfStringException.java | 12 +- .../AbstractWhitespaceCollapsingInvariantType.java | 46 ++++ .../axiom/datatype/xsd/NoTimeZoneException.java | 6 +- .../apache/axiom/datatype/xsd/SimpleTimeZone.java | 95 ++++++++ .../org/apache/axiom/datatype/xsd/Temporal.java | 26 ++- .../apache/axiom/datatype/xsd/TemporalImpl.java | 68 ++++++ .../apache/axiom/datatype/xsd/TemporalType.java | 245 +++++++++++++++++++++ .../java/org/apache/axiom/datatype/xsd/Util.java | 10 +- .../java/org/apache/axiom/datatype/xsd/XSDate.java | 6 +- .../org/apache/axiom/datatype/xsd/XSDateImpl.java | 106 +++++++++ .../org/apache/axiom/datatype/xsd/XSDateTime.java | 21 +- .../apache/axiom/datatype/xsd/XSDateTimeImpl.java | 129 +++++++++++ .../apache/axiom/datatype/xsd/XSDateTimeType.java | 8 +- .../axiom/datatype/xsd/XSDateTimeTypeImpl.java | 22 +- .../org/apache/axiom/datatype/xsd/XSDateType.java | 8 +- .../apache/axiom/datatype/xsd/XSDateTypeImpl.java | 21 +- .../org/apache/axiom/datatype/xsd/XSQNameType.java | 10 +- .../apache/axiom/datatype/xsd/XSQNameTypeImpl.java | 97 ++++++++ .../apache/axiom/datatype/xsd/XSStringType.java | 8 +- .../axiom/datatype/xsd/XSStringTypeImpl.java | 16 +- .../java/org/apache/axiom/datatype/xsd/XSTime.java | 5 +- .../org/apache/axiom/datatype/xsd/XSTimeImpl.java | 103 +++++++++ .../org/apache/axiom/datatype/xsd/XSTimeType.java | 8 +- .../apache/axiom/datatype/xsd/XSTimeTypeImpl.java | 21 +- .../org/apache/axiom/datatype/DOMHelperTest.java | 42 ++++ .../axiom/datatype/xsd/XSDateTimeTypeTest.java | 53 +++++ implementations/pom.xml | 1 + pom.xml | 1 + testing/soap-testsuite/pom.xml | 5 + .../axiom/ts/soap/ConvertedSOAPSampleContent.java | 57 +++-- 44 files changed, 1336 insertions(+), 114 deletions(-) diff --git a/aspects/core-aspects/pom.xml b/aspects/core-aspects/pom.xml index a4c1348..afb0952 100644 --- a/aspects/core-aspects/pom.xml +++ b/aspects/core-aspects/pom.xml @@ -40,6 +40,11 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>datatypes</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> </dependency> diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/aspects/core-aspects/src/main/java/org/apache/axiom/core/ContextAccessorImpl.java similarity index 56% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to aspects/core-aspects/src/main/java/org/apache/axiom/core/ContextAccessorImpl.java index 900ce36..d28636b 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/aspects/core-aspects/src/main/java/org/apache/axiom/core/ContextAccessorImpl.java @@ -16,8 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.core; -interface TextTransformer { - String transform(String in); +import org.apache.axiom.datatype.ContextAccessor; + +final class ContextAccessorImpl implements ContextAccessor<CoreElement,Semantics> { + public static final ContextAccessorImpl INSTANCE = new ContextAccessorImpl(); + + private ContextAccessorImpl() {} + + public String lookupNamespaceURI(CoreElement element, Semantics semantics, String prefix) { + return element.coreLookupNamespaceURI(prefix, semantics); + } + + public String lookupPrefix(CoreElement element, Semantics semantics, String namespaceURI) { + return element.coreLookupPrefix(namespaceURI, semantics); + } } diff --git a/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElement.java b/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElement.java index a5dd131..be5e79f 100644 --- a/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElement.java +++ b/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElement.java @@ -18,8 +18,11 @@ */ package org.apache.axiom.core; +import java.text.ParseException; import java.util.Iterator; +import org.apache.axiom.datatype.Type; + public interface CoreElement extends CoreChildNode, CoreMixedContentContainer, CoreNamedNode, DeferringParentNode { /** * Get the first attribute of this element. @@ -153,4 +156,6 @@ public interface CoreElement extends CoreChildNode, CoreMixedContentContainer, C // TODO: wrong Javadoc: null vs. empty string // TODO: we can support default namespaces! String coreLookupPrefix(String namespaceURI, Semantics semantics); + + <T> T coreGetValue(Type<T> type, Semantics semantics) throws ParseException; } diff --git a/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElementSupport.aj b/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElementSupport.aj index 2a271d2..e3e7f32 100644 --- a/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElementSupport.aj +++ b/aspects/core-aspects/src/main/java/org/apache/axiom/core/CoreElementSupport.aj @@ -18,8 +18,11 @@ */ package org.apache.axiom.core; +import java.text.ParseException; import java.util.Iterator; +import org.apache.axiom.datatype.Type; + public aspect CoreElementSupport { private CoreAttribute CoreElement.firstAttribute; @@ -220,4 +223,18 @@ public aspect CoreElementSupport { // This is basically a hook for OMSourcedElement public <T> void CoreElement.initSource(ClonePolicy<T> policy, T options, CoreElement other) { } + + public final <T> T CoreElement.coreGetValue(Type<T> type, Semantics semantics) throws ParseException { + Object characterData = coreGetCharacterData(ElementAction.RETURN_NULL); + if (characterData == null) { + throw new ParseException("Element has mixed content", 0); + } else { + return type.parse(characterData.toString(), ContextAccessorImpl.INSTANCE, this, semantics); + } + } + + public final <T> void CoreElement.coreSetValue(Type<T> type, T value, Semantics semantics) { + // TODO: actually set value + type.format(value, ContextAccessorImpl.INSTANCE, this, semantics); + } } diff --git a/aspects/fom-aspects/pom.xml b/aspects/fom-aspects/pom.xml index 3a66c31..e2b467c 100644 --- a/aspects/fom-aspects/pom.xml +++ b/aspects/fom-aspects/pom.xml @@ -43,6 +43,11 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>datatypes</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> </dependency> diff --git a/aspects/fom-aspects/src/main/java/org/apache/axiom/fom/AbderaDateTimeMixin.aj b/aspects/fom-aspects/src/main/java/org/apache/axiom/fom/AbderaDateTimeMixin.aj index 9bf2142..fb75b3b 100644 --- a/aspects/fom-aspects/src/main/java/org/apache/axiom/fom/AbderaDateTimeMixin.aj +++ b/aspects/fom-aspects/src/main/java/org/apache/axiom/fom/AbderaDateTimeMixin.aj @@ -17,19 +17,35 @@ */ package org.apache.axiom.fom; +import java.text.ParseException; import java.util.Calendar; import java.util.Date; import org.apache.abdera.model.AtomDate; import org.apache.abdera.model.DateTime; +import org.apache.axiom.datatype.xsd.XSDateTime; +import org.apache.axiom.datatype.xsd.XSDateTimeType; import org.apache.axiom.fom.AbderaDateTime; public aspect AbderaDateTimeMixin { private AtomDate AbderaDateTime.value; + private XSDateTime AbderaDateTime.getXSDateTime() { + XSDateTime dateTime; + try { + dateTime = coreGetValue(XSDateTimeType.INSTANCE, FOMSemantics.INSTANCE); + } catch (ParseException ex) { + throw new IllegalArgumentException("Invalid Date Format", ex); + } + if (!dateTime.hasTimeZone()) { + throw new IllegalArgumentException("Not a valid RFC3339 date/time value: no time zone"); + } + return dateTime; + } + public final AtomDate AbderaDateTime.getValue() { if (value == null) { - value = AtomDate.valueOf(getText()); + value = new AtomDate(getXSDateTime().getDate(null)); } return value; } diff --git a/aspects/core-aspects/pom.xml b/datatypes/pom.xml similarity index 61% copy from aspects/core-aspects/pom.xml copy to datatypes/pom.xml index a4c1348..9111070 100644 --- a/aspects/core-aspects/pom.xml +++ b/datatypes/pom.xml @@ -17,31 +17,46 @@ ~ specific language governing permissions and limitations ~ under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.apache.ws.commons.axiom</groupId> - <artifactId>aspects</artifactId> + <artifactId>axiom</artifactId> <version>1.2.16-SNAPSHOT</version> </parent> - <artifactId>core-aspects</artifactId> + <artifactId>datatypes</artifactId> <packaging>bundle</packaging> - <name>Core Aspects</name> <url>http://ws.apache.org/axiom/</url> <dependencies> <dependency> - <!-- This dependency will eventually go away --> - <groupId>${project.groupId}</groupId> - <artifactId>axiom-api</artifactId> - <version>${project.version}</version> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> </dependency> <dependency> - <groupId>org.aspectj</groupId> - <artifactId>aspectjrt</artifactId> + <groupId>com.google.truth</groupId> + <artifactId>truth</artifactId> + <scope>test</scope> </dependency> </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package>*;-noimport:=true</Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> </project> diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/AbstractInvariantType.java similarity index 63% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/AbstractInvariantType.java index 900ce36..30a4a97 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/AbstractInvariantType.java @@ -16,8 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +public abstract class AbstractInvariantType<T> implements InvariantType<T> { + public final <S,O> T parse(String literal, ContextAccessor<S,O> contextAccessor, S contextObject, O options) throws ParseException { + return parse(literal); + } + + public final <S,O> String format(T value, ContextAccessor<S,O> contextAccessor, S contextObject, O options) { + return format(value); + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/ContextAccessor.java similarity index 78% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/ContextAccessor.java index 900ce36..a384eb0 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/ContextAccessor.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +public interface ContextAccessor<T,O> { + String lookupNamespaceURI(T contextObject, O options, String prefix); + String lookupPrefix(T contextObject, O options, String namespaceURI); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/DOMContextAccessor.java similarity index 52% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/DOMContextAccessor.java index 900ce36..fc3ed9f 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/DOMContextAccessor.java @@ -16,8 +16,23 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import org.w3c.dom.Element; + +final class DOMContextAccessor implements ContextAccessor<Element,Void> { + static final DOMContextAccessor INSTANCE = new DOMContextAccessor(); + + public String lookupNamespaceURI(Element element, Void options, String prefix) { + String namespaceURI = element.lookupNamespaceURI(prefix.length() == 0 ? null : prefix); + if (namespaceURI != null) { + return namespaceURI; + } else { + return prefix.length() == 0 ? "" : null; + } + } + + public String lookupPrefix(Element element, Void options, String namespaceURI) { + return element.lookupPrefix(namespaceURI.length() == 0 ? null : namespaceURI); + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/DOMHelper.java similarity index 55% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/DOMHelper.java index 900ce36..46f13cc 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/DOMHelper.java @@ -16,8 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +import org.w3c.dom.Element; + +public final class DOMHelper { + private DOMHelper() {} + + public static <T> T getValue(Element element, Type<T> type) throws ParseException { + // TODO: using getTextContent here is actually incorrect because it extracts text recursively + return type.parse(element.getTextContent(), DOMContextAccessor.INSTANCE, element, null); + } + + public static <T> void setValue(Element element, Type<T> type, T value) { + element.setTextContent(type.format(value, DOMContextAccessor.INSTANCE, element, null)); + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/InvariantType.java similarity index 80% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/InvariantType.java index 900ce36..88faefe 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/InvariantType.java @@ -16,8 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +public interface InvariantType<T> extends Type<T> { + T parse(String literal) throws ParseException; + String format(T value); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/Type.java similarity index 71% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/Type.java index 900ce36..b5351b8 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/Type.java @@ -16,8 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +public interface Type<T> { + <S,O> T parse(String literal, ContextAccessor<S,O> contextAccessor, S contextObject, O options) throws ParseException; + <S,O> String format(T value, ContextAccessor<S,O> contextAccessor, S contextObject, O options); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/UnexpectedCharacterException.java similarity index 69% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/UnexpectedCharacterException.java index 900ce36..ac03ac4 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/UnexpectedCharacterException.java @@ -16,8 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +public class UnexpectedCharacterException extends ParseException { + private static final long serialVersionUID = 1L; + + public UnexpectedCharacterException(String literal, int errorOffset) { + super("Unexpected character '" + literal.charAt(errorOffset) + "'", errorOffset); + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/UnexpectedEndOfStringException.java similarity index 71% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/UnexpectedEndOfStringException.java index 900ce36..879f78d 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/UnexpectedEndOfStringException.java @@ -16,8 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +public class UnexpectedEndOfStringException extends ParseException { + private static final long serialVersionUID = 1L; + + public UnexpectedEndOfStringException(String literal) { + super("Unexpected end of string", literal.length()); + } } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/AbstractWhitespaceCollapsingInvariantType.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/AbstractWhitespaceCollapsingInvariantType.java new file mode 100644 index 0000000..03cd2d8 --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/AbstractWhitespaceCollapsingInvariantType.java @@ -0,0 +1,46 @@ +/* + * 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.axiom.datatype.xsd; + +import java.text.ParseException; + +import org.apache.axiom.datatype.AbstractInvariantType; +import org.apache.axiom.datatype.UnexpectedEndOfStringException; + +abstract class AbstractWhitespaceCollapsingInvariantType<T> extends AbstractInvariantType<T> { + public final T parse(String literal) throws ParseException { + final int len = literal.length(); + if (len == 0) { + throw new UnexpectedEndOfStringException(literal); + } + int begin = 0; + while (Util.isWhitespace(literal.charAt(begin))) { + if (++begin == len) { + throw new UnexpectedEndOfStringException(literal); + } + } + int end = len; + while (Util.isWhitespace(literal.charAt(end-1))) { + end--; + } + return parse(literal, begin, end); + } + + protected abstract T parse(String literal, int begin, int end) throws ParseException; +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/NoTimeZoneException.java similarity index 84% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/NoTimeZoneException.java index 900ce36..12d1d69 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/NoTimeZoneException.java @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +public class NoTimeZoneException extends RuntimeException { + private static final long serialVersionUID = 1L; } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/SimpleTimeZone.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/SimpleTimeZone.java new file mode 100644 index 0000000..28dd8bd --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/SimpleTimeZone.java @@ -0,0 +1,95 @@ +/* + * 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.axiom.datatype.xsd; + +import java.util.Date; +import java.util.TimeZone; + +final class SimpleTimeZone extends TimeZone { + private static final long serialVersionUID = 1L; + + private static final SimpleTimeZone[] instances; + + static { + instances = new SimpleTimeZone[57]; + for (int i=0; i<instances.length; i++) { + instances[i] = new SimpleTimeZone(30*(-28 + i)); + } + } + + private final int delta; + + SimpleTimeZone(int delta) { + this.delta = delta; + if (delta == 0) { + super.setID("GMT"); + } else { + StringBuilder buffer = new StringBuilder("GMT"); + buffer.append(delta < 0 ? '-' : '+'); + delta = Math.abs(delta); + append2Digits(buffer, delta/60); + buffer.append(':'); + append2Digits(buffer, delta % 60); + super.setID(buffer.toString()); + } + } + + private static void append2Digits(StringBuilder buffer, int value) { + buffer.append((char)('0' + value/10)); + buffer.append((char)('0' + value%10)); + } + + static SimpleTimeZone getInstance(int delta) { + return delta % 30 == 0 ? instances[delta/30 + 28] : new SimpleTimeZone(delta); + } + + @Override + public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) { + return getRawOffset(); + } + + @Override + public void setRawOffset(int offsetMillis) { + if (getRawOffset() != offsetMillis) { + throw new UnsupportedOperationException("Immutable time zone object"); + } + } + + @Override + public int getRawOffset() { + return delta * 60000; + } + + @Override + public boolean useDaylightTime() { + return false; + } + + @Override + public boolean inDaylightTime(Date date) { + return false; + } + + @Override + public void setID(String ID) { + if (!getID().equals(ID)) { + throw new UnsupportedOperationException("Immutable time zone object"); + } + } +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/Temporal.java similarity index 51% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/Temporal.java index 900ce36..0d808f6 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/Temporal.java @@ -16,8 +16,28 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +import java.util.GregorianCalendar; +import java.util.TimeZone; + +public interface Temporal { + /** + * + * @param defaultTimeZone + * the time zone to use if this temporal object has no time zone; may be + * <code>null</code> + * @return + * @throws NoTimeZoneException + * if this temporal object doesn't have a time zone and no default time zone was + * specified + */ + GregorianCalendar getCalendar(TimeZone defaultTimeZone); + + /** + * Determine if this temporal object has a time zone. + * + * @return <code>true</code> if the object has a time zone, <code>false</code> otherwise + */ + boolean hasTimeZone(); } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/TemporalImpl.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/TemporalImpl.java new file mode 100644 index 0000000..5e381c2 --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/TemporalImpl.java @@ -0,0 +1,68 @@ +/* + * 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.axiom.datatype.xsd; + +import java.util.GregorianCalendar; +import java.util.TimeZone; + +abstract class TemporalImpl implements Temporal { + abstract boolean hasDatePart(); + abstract boolean hasTimePart(); + abstract boolean isBC(); + abstract String getAeon(); + abstract int getYear(); + abstract int getMonth(); + abstract int getDay(); + abstract int getHour(); + abstract int getMinute(); + abstract int getSecond(); + abstract int getNanoSecond(); + abstract SimpleTimeZone getTimeZone(); + abstract String getNanoSecondFraction(); + + public final GregorianCalendar getCalendar(TimeZone defaultTimeZone) { + // TODO: check aeon + TimeZone timeZone = getTimeZone(); + if (timeZone == null) { + if (defaultTimeZone == null) { + throw new NoTimeZoneException(); + } + timeZone = defaultTimeZone; + } + GregorianCalendar calendar = new GregorianCalendar(timeZone); + if (hasDatePart()) { + // TODO: BC + // TODO: throw exception if aeon is not null + calendar.set(GregorianCalendar.YEAR, getYear()); + calendar.set(GregorianCalendar.MONTH, getMonth()-1); + calendar.set(GregorianCalendar.DAY_OF_MONTH, getDay()); + } + if (hasTimePart()) { + calendar.set(GregorianCalendar.HOUR_OF_DAY, getHour()); + calendar.set(GregorianCalendar.MINUTE, getMinute()); + calendar.set(GregorianCalendar.SECOND, getSecond()); + calendar.set(GregorianCalendar.MILLISECOND, getNanoSecond()/1000000); + } + return calendar; + } + + public boolean hasTimeZone() { + return getTimeZone() != null; + } +} diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/TemporalType.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/TemporalType.java new file mode 100644 index 0000000..342a449 --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/TemporalType.java @@ -0,0 +1,245 @@ +/* + * 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.axiom.datatype.xsd; + +import java.text.ParseException; + +import org.apache.axiom.datatype.UnexpectedCharacterException; +import org.apache.axiom.datatype.UnexpectedEndOfStringException; + +abstract class TemporalType<T> extends AbstractWhitespaceCollapsingInvariantType<T> { + abstract boolean hasDatePart(); + abstract boolean hasTimePart(); + abstract T createInstance(boolean bc, String aeon, int year, int month, int day, int hour, + int minute, int second, int nanoSecond, String nanoSecondFraction, + SimpleTimeZone timeZone); + + @Override + protected final T parse(String literal, int begin, int end) throws ParseException { + int index = begin; + boolean bc; + String aeon; + int year; + int month; + int day; + if (hasDatePart()) { + if (literal.charAt(0) == '-') { + bc = true; + index++; + } else { + bc = false; + } + { + int digits = countDigits(literal, index, "-", false); + if (digits < 4) { + throw new UnexpectedCharacterException(literal, index+digits); + } + if (digits > 4 && literal.charAt(index) == '0') { + throw new UnexpectedCharacterException(literal, index); + } + if (digits > 9) { + aeon = literal.substring(index, index+digits-9); + index += digits-9; + digits = 9; + } else { + aeon = null; + } + year = parseDigitsUnchecked(literal, index, digits); + if (year == 0) { + throw new ParseException("Year can't be zero", index); + } + index += digits + 1; + } + month = parseDigitsChecked(literal, index, 2); + index += 2; + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } + if (literal.charAt(index) != '-') { + throw new UnexpectedCharacterException(literal, index); + } + index++; + day = parseDigitsChecked(literal, index, 2); + index += 2; + } else { + bc = false; + aeon = null; + year = 0; + month = 0; + day = 0; + } + if (hasDatePart() && hasTimePart()) { + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } + if (literal.charAt(index) != 'T') { + throw new UnexpectedCharacterException(literal, index); + } + index++; + } + int hour; + int minute; + int second; + int nanoSecond; + String nanoSecondFraction; + if (hasTimePart()) { + hour = parseDigitsChecked(literal, index, 2); + index += 2; + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } + if (literal.charAt(index) != ':') { + throw new UnexpectedCharacterException(literal, index); + } + index++; + minute = parseDigitsChecked(literal, index, 2); + index += 2; + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } + if (literal.charAt(index) != ':') { + throw new UnexpectedCharacterException(literal, index); + } + index++; + second = parseDigitsChecked(literal, index, 2); + index += 2; + if (index < end && literal.charAt(index) == '.') { + index++; + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } + int digits = countDigits(literal, index, "Z+-", true); + if (digits == 0) { + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } else { + throw new UnexpectedCharacterException(literal, index); + } + } + // TODO: we should remove trailing zeros + nanoSecond = parseDigitsUnchecked(literal, index, Math.min(digits, 9)); + if (digits > 9) { + nanoSecondFraction = literal.substring(index+9, index+digits-9); + } else { + for (int i = digits; i < 9; i++) { + nanoSecond *= 10; + } + nanoSecondFraction = null; + } + index += digits; + } else { + nanoSecond = 0; + nanoSecondFraction = null; + } + } else { + hour = 0; + minute = 0; + second = 0; + nanoSecond = 0; + nanoSecondFraction = null; + } + SimpleTimeZone timeZone; + if (index == end) { + timeZone = null; + } else { + int delta; + char c = literal.charAt(index); + index++; + if (c == 'Z') { + delta = 0; + } else { + boolean negative = c == '-'; + int deltaHours = parseDigitsChecked(literal, index, 2); + index += 2; + if (index == end) { + throw new UnexpectedEndOfStringException(literal); + } + if (literal.charAt(index) != ':') { + throw new UnexpectedCharacterException(literal, index); + } + index++; + int deltaMinutes = parseDigitsChecked(literal, index, 2); + index += 2; + delta = 60*deltaHours + deltaMinutes; + if (negative) { + delta = -delta; + } + } + timeZone = SimpleTimeZone.getInstance(delta); + } + if (index != end) { + throw new UnexpectedCharacterException(literal, index); + } + return createInstance(bc, aeon, year, month, day, hour, minute, second, nanoSecond, + nanoSecondFraction, timeZone); + } + + private static int countDigits(String literal, int index, String stopChars, boolean allowEndOfString) throws ParseException { + final int len = literal.length(); + int count = 0; + while (true) { + if (index == len) { + if (allowEndOfString) { + return count; + } else { + throw new UnexpectedEndOfStringException(literal); + } + } + char c = literal.charAt(index); + if ('0' <= c && c <= '9') { + index++; + count++; + } else if (stopChars.indexOf(c) != -1) { + return count; + } else { + throw new UnexpectedCharacterException(literal, index); + } + } + } + + private static int parseDigitsUnchecked(String literal, int index, int count) { + int value = 0; + for (; count > 0; count--) { + value = 10*value + literal.charAt(index++) - '0'; + } + return value; + } + + private static int parseDigitsChecked(String literal, int index, int count) throws ParseException { + final int len = literal.length(); + int value = 0; + for (; count > 0; count--) { + if (index == len) { + throw new UnexpectedEndOfStringException(literal); + } + char c = literal.charAt(index); + if ('0' <= c && c <= '9') { + value = 10*value + c - '0'; + } else { + throw new UnexpectedCharacterException(literal, index); + } + index++; + } + return value; + } + + public final String format(T value) { + return value.toString(); + } +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/Util.java similarity index 80% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/Util.java index 900ce36..b244db7 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/Util.java @@ -16,8 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +final class Util { + private Util() {} + + static boolean isWhitespace(char c) { + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDate.java similarity index 87% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDate.java index 900ce36..45bef69 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDate.java @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +public interface XSDate extends Temporal { + XSDateTime getDayStart(); } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateImpl.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateImpl.java new file mode 100644 index 0000000..4eb748a --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateImpl.java @@ -0,0 +1,106 @@ +/* + * 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.axiom.datatype.xsd; + +final class XSDateImpl extends TemporalImpl implements XSDate { + private final boolean bc; + private final String aeon; + private final int year; + private final int month; + private final int day; + private final SimpleTimeZone timeZone; + + XSDateImpl(boolean bc, String aeon, int year, int month, int day, SimpleTimeZone timeZone) { + this.bc = bc; + this.aeon = aeon; + this.year = year; + this.month = month; + this.day = day; + this.timeZone = timeZone; + } + + @Override + boolean hasDatePart() { + return true; + } + + @Override + boolean hasTimePart() { + return false; + } + + @Override + boolean isBC() { + return bc; + } + + @Override + String getAeon() { + return aeon; + } + + @Override + int getYear() { + return year; + } + + @Override + int getMonth() { + return month; + } + + @Override + int getDay() { + return day; + } + + @Override + int getHour() { + throw new UnsupportedOperationException(); + } + + @Override + int getMinute() { + throw new UnsupportedOperationException(); + } + + @Override + int getSecond() { + throw new UnsupportedOperationException(); + } + + @Override + int getNanoSecond() { + throw new UnsupportedOperationException(); + } + + @Override + String getNanoSecondFraction() { + throw new UnsupportedOperationException(); + } + + @Override + SimpleTimeZone getTimeZone() { + return timeZone; + } + + public XSDateTime getDayStart() { + return new XSDateTimeImpl(bc, aeon, year, month, day, 0, 0, 0, 0, null, timeZone); + } +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTime.java similarity index 56% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTime.java index 900ce36..dbf45db 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTime.java @@ -16,8 +16,23 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +import java.util.Date; +import java.util.TimeZone; + +public interface XSDateTime extends Temporal { + XSDate getDate(); + XSTime getTime(); + + /** + * Convert this date/time to a {@link Date} object. + * + * @param defaultTimeZone + * the time zone to use if this date/time has no time zone; may be <code>null</code> + * @return the {@link Date} object + * @throws NoTimeZoneException + * if this date/time doesn't have a time zone and no default time zone was specified + */ + Date getDate(TimeZone defaultTimeZone); } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeImpl.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeImpl.java new file mode 100644 index 0000000..6d33144 --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeImpl.java @@ -0,0 +1,129 @@ +/* + * 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.axiom.datatype.xsd; + +import java.util.Date; +import java.util.TimeZone; + +final class XSDateTimeImpl extends TemporalImpl implements XSDateTime { + private final boolean bc; + private final String aeon; + private final int year; + private final int month; + private final int day; + private final int hour; + private final int minute; + private final int second; + private final int nanoSecond; + private final String nanoSecondFraction; + private final SimpleTimeZone timeZone; + + XSDateTimeImpl(boolean bc, String aeon, int year, int month, int day, int hour, int minute, + int second, int nanoSecond, String nanoSecondFraction, SimpleTimeZone timeZone) { + this.bc = bc; + this.aeon = aeon; + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + this.nanoSecond = nanoSecond; + this.nanoSecondFraction = nanoSecondFraction; + this.timeZone = timeZone; + } + + @Override + boolean hasDatePart() { + return true; + } + + @Override + boolean hasTimePart() { + return true; + } + + @Override + boolean isBC() { + return bc; + } + + @Override + String getAeon() { + return aeon; + } + + @Override + int getYear() { + return year; + } + + @Override + int getMonth() { + return month; + } + + @Override + int getDay() { + return day; + } + + @Override + int getHour() { + return hour; + } + + @Override + int getMinute() { + return minute; + } + + @Override + int getSecond() { + return second; + } + + @Override + int getNanoSecond() { + return nanoSecond; + } + + @Override + String getNanoSecondFraction() { + return nanoSecondFraction; + } + + @Override + SimpleTimeZone getTimeZone() { + return timeZone; + } + + public XSDate getDate() { + return new XSDateImpl(bc, aeon, year, month, day, timeZone); + } + + public XSTime getTime() { + return new XSTimeImpl(hour, minute, second, nanoSecond, nanoSecondFraction, timeZone); + } + + public Date getDate(TimeZone defaultTimeZone) { + // TODO: fast path for dates in the 20th and 21st century + return getCalendar(defaultTimeZone).getTime(); + } +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeType.java similarity index 79% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeType.java index 900ce36..5b3cbb4 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeType.java @@ -16,8 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +import org.apache.axiom.datatype.InvariantType; + +public interface XSDateTimeType extends InvariantType<XSDateTime> { + XSDateTimeType INSTANCE = new XSDateTimeTypeImpl(); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeTypeImpl.java similarity index 55% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeTypeImpl.java index 900ce36..8d19cda 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTimeTypeImpl.java @@ -16,8 +16,24 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +final class XSDateTimeTypeImpl extends TemporalType<XSDateTime> implements XSDateTimeType { + @Override + boolean hasDatePart() { + return true; + } + + @Override + boolean hasTimePart() { + return true; + } + + @Override + XSDateTime createInstance(boolean bc, String aeon, int year, int month, int day, int hour, + int minute, int second, int nanoSecond, String nanoSecondFraction, + SimpleTimeZone timeZone) { + return new XSDateTimeImpl(bc, aeon, year, month, day, hour, minute, second, nanoSecond, + nanoSecondFraction, timeZone); + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateType.java similarity index 84% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateType.java index 900ce36..e982540 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateType.java @@ -16,8 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; + +import org.apache.axiom.datatype.InvariantType; + +public interface XSDateType extends InvariantType<XSDate> { -interface TextTransformer { - String transform(String in); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTypeImpl.java similarity index 59% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTypeImpl.java index 900ce36..901a798 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSDateTypeImpl.java @@ -16,8 +16,23 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +final class XSDateTypeImpl extends TemporalType<XSDate> implements XSDateType { + @Override + boolean hasDatePart() { + return true; + } + + @Override + boolean hasTimePart() { + return false; + } + + @Override + XSDate createInstance(boolean bc, String aeon, int year, int month, int day, int hour, + int minute, int second, int nanoSecond, String nanoSecondFraction, + SimpleTimeZone timeZone) { + return new XSDateImpl(bc, aeon, year, month, day, timeZone); + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSQNameType.java similarity index 78% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSQNameType.java index 900ce36..c8497ac 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSQNameType.java @@ -16,8 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +import javax.xml.namespace.QName; + +import org.apache.axiom.datatype.Type; + +public interface XSQNameType extends Type<QName> { + XSQNameType INSTANCE = new XSQNameTypeImpl(); } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSQNameTypeImpl.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSQNameTypeImpl.java new file mode 100644 index 0000000..509dcca --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSQNameTypeImpl.java @@ -0,0 +1,97 @@ +/* + * 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.axiom.datatype.xsd; + +import java.text.ParseException; + +import javax.xml.namespace.QName; + +import org.apache.axiom.datatype.ContextAccessor; +import org.apache.axiom.datatype.UnexpectedCharacterException; +import org.apache.axiom.datatype.UnexpectedEndOfStringException; + +final class XSQNameTypeImpl implements XSQNameType { + public <S,O> QName parse(String literal, ContextAccessor<S,O> contextAccessor, S contextObject, O options) + throws ParseException { + int len = literal.length(); + int start = -1; + int end = -1; + int colonIndex = -1; + for (int index = 0; index<len; index++) { + char c = literal.charAt(index); + if (Util.isWhitespace(c)) { + if (start != -1 && end == -1) { + end = index; + } + } else { + if (start == -1) { + start = index; + } else if (end != -1) { + throw new UnexpectedCharacterException(literal, index); + } + // TODO: we should check that the literal is a valid NCName + if (literal.charAt(index) == ':') { + if (colonIndex != -1) { + throw new UnexpectedCharacterException(literal, index); + } + colonIndex = index; + } + } + } + if (start == -1) { + throw new UnexpectedEndOfStringException(literal); + } + if (end == -1) { + end = len; + } + String prefix; + String localPart; + if (colonIndex == -1) { + prefix = ""; + localPart = literal.toString(); + } else { + prefix = literal.substring(start, colonIndex); + localPart = literal.substring(colonIndex+1, end); + } + String namespaceURI = contextAccessor.lookupNamespaceURI(contextObject, options, prefix); + if (namespaceURI == null) { + throw new ParseException("Unbound namespace prefix \"" + prefix + "\"", 0); + } + return new QName(namespaceURI, localPart, prefix); + } + + public <S,O> String format(QName value, ContextAccessor<S,O> contextAccessor, S contextObject, O options) { + String prefix = value.getPrefix(); + String namespaceURI = value.getNamespaceURI(); + if (!namespaceURI.equals(contextAccessor.lookupNamespaceURI(contextObject, options, prefix))) { + String existingPrefix = contextAccessor.lookupPrefix(contextObject, options, namespaceURI); + if (existingPrefix != null) { + prefix = existingPrefix; + } else { + // TODO + throw new RuntimeException(); + } + } + if (prefix.length() == 0) { + return value.getLocalPart(); + } else { + return prefix + ":" + value.getLocalPart(); + } + } +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSStringType.java similarity index 79% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSStringType.java index 900ce36..068656e 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSStringType.java @@ -16,8 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +import org.apache.axiom.datatype.InvariantType; + +public interface XSStringType extends InvariantType<String> { + XSStringType INSTANCE = new XSStringTypeImpl(); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSStringTypeImpl.java similarity index 67% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSStringTypeImpl.java index 900ce36..7c08cc6 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSStringTypeImpl.java @@ -16,8 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +import java.text.ParseException; + +import org.apache.axiom.datatype.AbstractInvariantType; + +final class XSStringTypeImpl extends AbstractInvariantType<String> implements XSStringType { + public String parse(String literal) throws ParseException { + return literal; + } + + public String format(String value) { + return value; + } } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTime.java similarity index 89% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTime.java index 900ce36..dda1343 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTime.java @@ -16,8 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +public interface XSTime extends Temporal { } diff --git a/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeImpl.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeImpl.java new file mode 100644 index 0000000..5b2e347 --- /dev/null +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeImpl.java @@ -0,0 +1,103 @@ +/* + * 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.axiom.datatype.xsd; + +final class XSTimeImpl extends TemporalImpl implements XSTime { + private final int hour; + private final int minute; + private final int second; + private final int nanoSecond; + private final String nanoSecondFraction; + private final SimpleTimeZone timeZone; + + XSTimeImpl(int hour, int minute, int second, int nanoSecond, String nanoSecondFraction, + SimpleTimeZone timeZone) { + this.hour = hour; + this.minute = minute; + this.second = second; + this.nanoSecond = nanoSecond; + this.nanoSecondFraction = nanoSecondFraction; + this.timeZone = timeZone; + } + + @Override + boolean hasDatePart() { + return false; + } + + @Override + boolean hasTimePart() { + return true; + } + + @Override + boolean isBC() { + throw new UnsupportedOperationException(); + } + + @Override + String getAeon() { + throw new UnsupportedOperationException(); + } + + @Override + int getYear() { + throw new UnsupportedOperationException(); + } + + @Override + int getMonth() { + throw new UnsupportedOperationException(); + } + + @Override + int getDay() { + throw new UnsupportedOperationException(); + } + + @Override + int getHour() { + return hour; + } + + @Override + int getMinute() { + return minute; + } + + @Override + int getSecond() { + return second; + } + + @Override + int getNanoSecond() { + return nanoSecond; + } + + @Override + String getNanoSecondFraction() { + return nanoSecondFraction; + } + + @Override + SimpleTimeZone getTimeZone() { + return timeZone; + } +} diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeType.java similarity index 84% copy from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java copy to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeType.java index 900ce36..1d9c8ac 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeType.java @@ -16,8 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; + +import org.apache.axiom.datatype.InvariantType; + +public interface XSTimeType extends InvariantType<XSTime> { -interface TextTransformer { - String transform(String in); } diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeTypeImpl.java similarity index 57% rename from testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java rename to datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeTypeImpl.java index 900ce36..039945d 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/TextTransformer.java +++ b/datatypes/src/main/java/org/apache/axiom/datatype/xsd/XSTimeTypeImpl.java @@ -16,8 +16,23 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.axiom.ts.soap; +package org.apache.axiom.datatype.xsd; -interface TextTransformer { - String transform(String in); +final class XSTimeTypeImpl extends TemporalType<XSTime> implements XSTimeType { + @Override + boolean hasDatePart() { + return false; + } + + @Override + boolean hasTimePart() { + return true; + } + + @Override + XSTime createInstance(boolean bc, String aeon, int year, int month, int day, int hour, + int minute, int second, int nanoSecond, String nanoSecondFraction, + SimpleTimeZone timeZone) { + return new XSTimeImpl(hour, minute, second, nanoSecond, nanoSecondFraction, timeZone); + } } diff --git a/datatypes/src/test/java/org/apache/axiom/datatype/DOMHelperTest.java b/datatypes/src/test/java/org/apache/axiom/datatype/DOMHelperTest.java new file mode 100644 index 0000000..97e3fab --- /dev/null +++ b/datatypes/src/test/java/org/apache/axiom/datatype/DOMHelperTest.java @@ -0,0 +1,42 @@ +/* + * 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.axiom.datatype; + +import static com.google.common.truth.Truth.assertThat; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.axiom.datatype.xsd.XSQNameType; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class DOMHelperTest { + @Test + public void testGetQNameFromElement() throws Exception { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element element = document.createElementNS("urn:test", "p:elem"); + element.setTextContent("p:value"); + QName qname = DOMHelper.getValue(element, XSQNameType.INSTANCE); + assertThat(qname.getNamespaceURI()).isEqualTo("urn:test"); + assertThat(qname.getLocalPart()).isEqualTo("value"); + assertThat(qname.getPrefix()).isEqualTo("p"); + } +} diff --git a/datatypes/src/test/java/org/apache/axiom/datatype/xsd/XSDateTimeTypeTest.java b/datatypes/src/test/java/org/apache/axiom/datatype/xsd/XSDateTimeTypeTest.java new file mode 100644 index 0000000..eb81dad --- /dev/null +++ b/datatypes/src/test/java/org/apache/axiom/datatype/xsd/XSDateTimeTypeTest.java @@ -0,0 +1,53 @@ +/* + * 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.axiom.datatype.xsd; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import java.util.Date; + +import org.apache.axiom.datatype.UnexpectedEndOfStringException; +import org.junit.Test; + +public class XSDateTimeTypeTest { + @Test + public void testParse() throws Exception { + XSDateTime dateTime = XSDateTimeType.INSTANCE.parse("2003-12-13T18:30:02Z"); + assertThat(dateTime.getDate(null)).isEqualTo(new Date(1071340202000L)); + } + + @Test + public void testParseTruncated() throws Exception { + String literal = "2002-10-10T12:00:00-05:00"; + for (int i=0; i<literal.length()-1; i++) { + if (i == 19) { + // This would give a valid literal without time zone + continue; + } + String truncatedLiteral = literal.substring(0, i); + try { + XSDateTimeType.INSTANCE.parse(truncatedLiteral); + fail("Expected UnexpectedEndOfStringException for literal \"" + truncatedLiteral + "\""); + } catch (UnexpectedEndOfStringException ex) { + // Expected + } + } + } +} diff --git a/implementations/pom.xml b/implementations/pom.xml index 7c6572d..24a48e6 100644 --- a/implementations/pom.xml +++ b/implementations/pom.xml @@ -88,6 +88,7 @@ <artifactSet> <includes> <include>${project.groupId}:*-aspects</include> + <include>${project.groupId}:datatypes</include> <include>org.aspectj:aspectjrt</include> </includes> </artifactSet> diff --git a/pom.xml b/pom.xml index a145f0a..529056a 100644 --- a/pom.xml +++ b/pom.xml @@ -223,6 +223,7 @@ <module>testing</module> <module>aspects</module> <module>implementations</module> + <module>datatypes</module> </modules> <scm> diff --git a/testing/soap-testsuite/pom.xml b/testing/soap-testsuite/pom.xml index 1d299b4..658022a 100644 --- a/testing/soap-testsuite/pom.xml +++ b/testing/soap-testsuite/pom.xml @@ -45,6 +45,11 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>datatypes</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> diff --git a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/ConvertedSOAPSampleContent.java b/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/ConvertedSOAPSampleContent.java index 983487d..36063c9 100644 --- a/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/ConvertedSOAPSampleContent.java +++ b/testing/soap-testsuite/src/main/java/org/apache/axiom/ts/soap/ConvertedSOAPSampleContent.java @@ -20,6 +20,7 @@ package org.apache.axiom.ts.soap; import java.io.InputStream; import java.io.OutputStream; +import java.text.ParseException; import java.util.HashMap; import java.util.Map; @@ -30,6 +31,8 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import org.apache.axiom.datatype.DOMHelper; +import org.apache.axiom.datatype.xsd.XSQNameType; import org.apache.axiom.testing.multiton.Multiton; import org.apache.axiom.ts.xml.ComputedMessageContent; import org.w3c.dom.Attr; @@ -40,15 +43,15 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; final class ConvertedSOAPSampleContent extends ComputedMessageContent { - private static Map<String,String> faultCodeMap = new HashMap<String,String>(); + private static Map<QName,QName> faultCodeMap = new HashMap<>(); static { faultCodeMap.put( - SOAPSpec.SOAP12.getSenderFaultCode().getLocalPart(), - SOAPSpec.SOAP11.getSenderFaultCode().getLocalPart()); + SOAPSpec.SOAP12.getSenderFaultCode(), + SOAPSpec.SOAP11.getSenderFaultCode()); faultCodeMap.put( - SOAPSpec.SOAP12.getReceiverFaultCode().getLocalPart(), - SOAPSpec.SOAP11.getReceiverFaultCode().getLocalPart()); + SOAPSpec.SOAP12.getReceiverFaultCode(), + SOAPSpec.SOAP11.getReceiverFaultCode()); } private final SOAPSample soap12Message; @@ -112,25 +115,17 @@ final class ConvertedSOAPSampleContent extends ComputedMessageContent { child = nextChild; } } else if (type == SOAPFaultChild.CODE) { - final Element value = getChild(element, SOAPFaultChild.VALUE); - element.setTextContent(transform(value.getTextContent(), new TextTransformer() { - @Override - public String transform(String in) { - int idx = in.indexOf(':'); - if (idx == -1) { - return in; - } - String prefix = in.substring(0, idx); - if (!SOAPSpec.SOAP12.getEnvelopeNamespaceURI().equals(value.lookupNamespaceURI(prefix))) { - return in; - } - String newCode = faultCodeMap.get(in.substring(idx+1)); - if (newCode == null) { - return in; - } - return prefix + ":" + newCode; - } - })); + Element value = getChild(element, SOAPFaultChild.VALUE); + String[] whitespace = getSurroundingWhitespace(value.getTextContent()); + QName qname; + try { + qname = DOMHelper.getValue(value, XSQNameType.INSTANCE); + } catch (ParseException ex) { + throw new Error(ex); + } + QName newQName = faultCodeMap.get(qname); + DOMHelper.setValue(element, XSQNameType.INSTANCE, newQName == null ? qname : newQName); + element.setTextContent(reapplyWhitespace(element.getTextContent(), whitespace)); } else if (type == SOAPFaultChild.REASON) { Element text = getChild(element, SOAPFaultChild.TEXT); element.setTextContent(text.getTextContent()); @@ -210,19 +205,19 @@ final class ConvertedSOAPSampleContent extends ComputedMessageContent { return " \r\n\t".indexOf(c) != -1; } - private static String transform(String text, TextTransformer transformer) { + private static String[] getSurroundingWhitespace(String text) { int start = 0; while (isWhitespace(text.charAt(start))) { - if (++start == text.length()) { - return text; - } + start++; } int end = text.length(); while (isWhitespace(text.charAt(end-1))) { end--; } - return text.substring(0, start) - + transformer.transform(text.substring(start, end)) - + text.substring(end); + return new String[] { text.substring(0, start), text.substring(end) }; + } + + private static String reapplyWhitespace(String text, String[] surroundingWhitespace) { + return surroundingWhitespace[0] + text + surroundingWhitespace[1]; } }