Merge remote-tracking branch 'origin/master' into OLINGO-377-FIT

This commit is contained in:
Michael Bolz 2014-07-29 10:00:12 +02:00
commit 3aab1c1276
5 changed files with 70 additions and 70 deletions

View File

@ -307,7 +307,6 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
return res;
} else {
if (propertyChanges.containsKey(name)) {
res = propertyChanges.get(name);
} else if (propertyCache.containsKey(name)) {
@ -325,7 +324,6 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
false);
} else if (ref != null && ComplexCollection.class.isAssignableFrom(ref)) {
final ComplexCollectionInvocationHandler<?> collectionHandler;
final Class<?> itemRef = ClassUtils.extractTypeArg(ref, ComplexCollection.class);
@ -361,8 +359,7 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
new Class<?>[] {ref}, collectionHandler);
} else if (ref != null && PrimitiveCollection.class.isAssignableFrom(ref)) {
final PrimitiveCollectionInvocationHandler collectionHandler;
PrimitiveCollectionInvocationHandler collectionHandler;
if (property == null || property.hasNullValue()) {
collectionHandler = new PrimitiveCollectionInvocationHandler(
service,

View File

@ -196,12 +196,12 @@ final class OperationInvocationHandler extends AbstractInvocationHandler impleme
return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] {ClassUtils.getTypeClass(method.getGenericReturnType())}, new InvokerHandler(
edmOperation.getKey(),
parameterValues,
operation,
edmOperation.getValue(),
ClassUtils.getTypeArguments(method.getGenericReturnType()),
service));
edmOperation.getKey(),
parameterValues,
operation,
edmOperation.getValue(),
ClassUtils.getTypeArguments(method.getGenericReturnType()),
service));
} else {
throw new NoSuchMethodException(method.getName());
}
@ -228,50 +228,50 @@ final class OperationInvocationHandler extends AbstractInvocationHandler impleme
private Map.Entry<URI, EdmOperation> getBoundOperation(final Operation operation, final List<String> parameterNames) {
final CommonODataEntity entity = EntityInvocationHandler.class.cast(target).getEntity();
final URI entityURI = EntityInvocationHandler.class.cast(target).getEntityURI();
ODataOperation boundOp = entity.getOperation(operation.name());
if (boundOp == null) {
boundOp = entity.getOperation(new FullQualifiedName(targetFQN.getNamespace(), operation.name()).toString());
}
boolean useOperationFQN = this.getClient().getConfiguration().isUseUrlOperationFQN();
final boolean useOperationFQN = this.getClient().getConfiguration().isUseUrlOperationFQN();
EdmEntityType entityType = getClient().getCachedEdm().getEntityType(entity.getTypeName());
EdmEntityType baseType = entityType;
while (boundOp == null && baseType != null) {
// json minimal/none metadata doesn't return operations for entity, so here try creating it from Edm:
EdmAction action = this.getClient().getCachedEdm().getBoundAction(
final EdmAction action = this.getClient().getCachedEdm().getBoundAction(
new FullQualifiedName(targetFQN.getNamespace(), operation.name()),
baseType.getFullQualifiedName(),
false);
if (action != null) {
if (action == null) {
baseType = baseType.getBaseType();
} else {
boundOp = new ODataOperation();
boundOp.setMetadataAnchor(action.getFullQualifiedName().toString());
boundOp.setTitle(boundOp.getMetadataAnchor());
boundOp.setTarget(URI.create(entity.getEditLink().toString() + "/"
boundOp.setTarget(URI.create(entityURI.toASCIIString() + "/"
+ (useOperationFQN ? action.getFullQualifiedName().toString() : operation.name())));
} else {
baseType = baseType.getBaseType();
}
}
baseType = entityType;
while (boundOp == null && baseType != null) {
// json minimal/none metadata doesn't return operations for entity, so here try creating it from Edm:
EdmFunction func = this.getClient().getCachedEdm().getBoundFunction(
final EdmFunction func = this.getClient().getCachedEdm().getBoundFunction(
new FullQualifiedName(targetFQN.getNamespace(), operation.name()), baseType.getFullQualifiedName(),
false, parameterNames);
if (func != null) {
if (func == null) {
baseType = baseType.getBaseType();
} else {
boundOp = new ODataOperation();
boundOp.setMetadataAnchor(func.getFullQualifiedName().toString());
boundOp.setTitle(boundOp.getMetadataAnchor());
boundOp.setTarget(URI.create((entity.getEditLink() == null
? EntityInvocationHandler.class.cast(target).getEntityURI() : entity.getEditLink()).toString() + "/"
boundOp.setTarget(URI.create(entityURI.toASCIIString() + "/"
+ (useOperationFQN ? func.getFullQualifiedName().toString() : operation.name())));
} else {
baseType = baseType.getBaseType();
}
}
if (boundOp == null) {
@ -287,9 +287,9 @@ final class OperationInvocationHandler extends AbstractInvocationHandler impleme
while (edmOperation == null && entityType != null) {
edmOperation = operation.type() == OperationType.FUNCTION
? getClient().getCachedEdm().getBoundFunction(
operationFQN, entityType.getFullQualifiedName(), false, parameterNames)
operationFQN, entityType.getFullQualifiedName(), false, parameterNames)
: getClient().getCachedEdm().getBoundAction(
operationFQN, entityType.getFullQualifiedName(), false);
operationFQN, entityType.getFullQualifiedName(), false);
if (entityType.getBaseType() != null) {
entityType = entityType.getBaseType();
}
@ -318,6 +318,6 @@ final class OperationInvocationHandler extends AbstractInvocationHandler impleme
return new AbstractMap.SimpleEntry<URI, EdmOperation>(
URI.create(((EntityCollectionInvocationHandler<?>) target).getURI().toASCIIString()
+ "/" + edmOperation.getName()), edmOperation);
+ "/" + edmOperation.getName()), edmOperation);
}
}

View File

@ -361,8 +361,8 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
}
@Test
public void getProductDetails() {
Product product = getContainer().newEntityInstance(Product.class);
public void boundOperationsAfterCreate() {
final Product product = getContainer().newEntityInstance(Product.class);
product.setProductID(1012);
product.setName("Latte");
product.setQuantityPerUnit("100g Bag");
@ -390,9 +390,14 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
product.setDetails(detailCollection);
getContainer().getProducts().add(product);
getContainer().flush(); // The first HTTP Request to create product and the linked product detail
// The first HTTP Request to create product and the linked product detail
getContainer().flush();
// the second HTTP Request to execute getProductDetails() operation.
// The second HTTP request to access a bound operation via the local object
assertNotNull(product.operations().addAccessRight(AccessLevel.None).execute());
// The third HTTP Request to access a bound operation via entity URL
final StructuredCollectionInvoker<ProductDetailCollection> result =
container.getProducts().getByKey(1012).operations().getProductDetails(1);
assertEquals(1, result.execute().size());
@ -405,7 +410,7 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
// ---------------------------------------
org.apache.olingo.fit.proxy.v3.staticservice.Service<org.apache.olingo.client.api.v3.EdmEnabledODataClient> v3serv =
org.apache.olingo.fit.proxy.v3.staticservice.Service.getV3(
"http://localhost:9080/stub/StaticService/V30/Static.svc");
"http://localhost:9080/stub/StaticService/V30/Static.svc");
v3serv.getClient().getConfiguration().setDefaultBatchAcceptFormat(ContentType.APPLICATION_OCTET_STREAM);
final DefaultContainer v3cont = v3serv.getEntityContainer(DefaultContainer.class);
assertNotNull(v3cont);
@ -456,7 +461,6 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
// container.getOrders().getByKey(1).getCustomerForOrder().getEmails().execute().isEmpty());
// Not supported by the test service BTW generates a single request as expected:
// <service root>/Orders(1)/CustomerForOrder/Emails
emails.add("fabio.martelli@tirasa.net");
container.getPeople().getByKey(1).setEmails(emails);

View File

@ -38,9 +38,8 @@ public interface CUDRequestFactory extends CommonCUDRequestFactory<UpdateType> {
* appropriate format document for details. On successful completion, the response MUST be 204 No Content and contain
* an empty body.
*
* @param <E> concrete ODataEntity implementation
* @param targetURI entity set URI.
* @param entity entity to be created.
* @param targetURI entity set URI
* @param reference entity reference
* @return new ODataEntityCreateRequest instance.
*/
ODataReferenceAddingRequest getReferenceAddingRequest(URI targetURI, URI reference);

View File

@ -41,7 +41,7 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
};
private static final Pattern PATTERN = Pattern.compile(
"(-?\\p{Digit}{4,})-(\\p{Digit}{2})-(\\p{Digit}{2})"
"(-?\\p{Digit}{4,})-(\\p{Digit}{2})-(\\p{Digit}{2})"
+ "T(\\p{Digit}{2}):(\\p{Digit}{2})(?::(\\p{Digit}{2})(\\.(\\p{Digit}{0,12}?)0*)?)?"
+ "(Z|([-+]\\p{Digit}{2}:\\p{Digit}{2}))?");
@ -58,51 +58,51 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
@Override
protected <T> T internalValueOfString(final String value,
final Boolean isNullable, final Integer maxLength, final Integer precision,
final Integer scale, final Boolean isUnicode, final Class<T> returnType) throws EdmPrimitiveTypeException {
final Boolean isNullable, final Integer maxLength, final Integer precision,
final Integer scale, final Boolean isUnicode, final Class<T> returnType) throws EdmPrimitiveTypeException {
final Matcher matcher = PATTERN.matcher(value);
if (!matcher.matches()) {
throw new EdmPrimitiveTypeException("EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)");
}
final String timeZoneOffset = matcher.group(9) != null && matcher.group(10) != null
&& !matcher.group(10).matches("[-+]0+:0+") ? matcher.group(10) : null;
final String timeZoneOffset = matcher.group(9) == null || matcher.group(10) == null
|| matcher.group(10).matches("[-+]0+:0+") ? null : matcher.group(10);
final Calendar dateTimeValue = Calendar.getInstance(TimeZone.getTimeZone("GMT" + timeZoneOffset));
if (dateTimeValue.get(Calendar.ZONE_OFFSET) == 0 && timeZoneOffset != null) {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)");
"EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)");
}
dateTimeValue.clear();
dateTimeValue.set(
Short.parseShort(matcher.group(1)),
Byte.parseByte(matcher.group(2)) - 1, // month is zero-based
Byte.parseByte(matcher.group(3)),
Byte.parseByte(matcher.group(4)),
Byte.parseByte(matcher.group(5)),
matcher.group(6) == null ? 0 : Byte.parseByte(matcher.group(6)));
Short.parseShort(matcher.group(1)),
Byte.parseByte(matcher.group(2)) - 1, // month is zero-based
Byte.parseByte(matcher.group(3)),
Byte.parseByte(matcher.group(4)),
Byte.parseByte(matcher.group(5)),
matcher.group(6) == null ? 0 : Byte.parseByte(matcher.group(6)));
int nanoSeconds = 0;
if (matcher.group(7) != null) {
if (matcher.group(7).length() == 1 || matcher.group(7).length() > 13) {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)");
"EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)");
}
final String decimals = matcher.group(8);
if (decimals.length() > (precision == null ? 0 : precision)) {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.LITERAL_FACETS_NOT_MATCHED.addContent(value, facets)");
"EdmPrimitiveTypeException.LITERAL_FACETS_NOT_MATCHED.addContent(value, facets)");
}
if (returnType.isAssignableFrom(Timestamp.class)) {
if (!decimals.isEmpty()) {
nanoSeconds = Integer.parseInt(decimals.length() > 9 ? decimals.substring(0, 9) :
decimals + "000000000".substring(decimals.length()));
nanoSeconds = Integer.parseInt(decimals.length() > 9 ? decimals.substring(0, 9)
: decimals + "000000000".substring(decimals.length()));
}
} else {
final String milliSeconds = decimals.length() > 3 ?
decimals.substring(0, 3) :
decimals + "000".substring(decimals.length());
final String milliSeconds = decimals.length() > 3
? decimals.substring(0, 3)
: decimals + "000".substring(decimals.length());
dateTimeValue.set(Calendar.MILLISECOND, Short.parseShort(milliSeconds));
}
}
@ -111,26 +111,26 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
return convertDateTime(dateTimeValue, nanoSeconds, returnType);
} catch (final IllegalArgumentException e) {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)", e);
"EdmPrimitiveTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)", e);
} catch (final ClassCastException e) {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(returnType)", e);
"EdmPrimitiveTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(returnType)", e);
}
}
/**
* Converts a {@link Calendar} value into the requested return type if possible.
*
*
* @param dateTimeValue the value
* @param nanoSeconds nanoseconds part of the value; only used for the {@link Timestamp} return type
* @param returnType the class of the returned value;
* it must be one of {@link Calendar}, {@link Long}, {@link Date}, or {@link Timestamp}
* @param returnType the class of the returned value; it must be one of {@link Calendar}, {@link Long}, {@link Date},
* or {@link Timestamp}
* @return the converted value
* @throws IllegalArgumentException if the Calendar value is not valid
* @throws ClassCastException if the return type is not allowed
*/
protected static <T> T convertDateTime(final Calendar dateTimeValue, final int nanoSeconds,
final Class<T> returnType) throws IllegalArgumentException, ClassCastException {
final Class<T> returnType) throws IllegalArgumentException, ClassCastException {
// The Calendar class does not check any values until a get method is called,
// so we do just that to validate the fields that may have been set,
@ -159,8 +159,8 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
@Override
protected <T> String internalValueToString(final T value,
final Boolean isNullable, final Integer maxLength, final Integer precision,
final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Boolean isNullable, final Integer maxLength, final Integer precision,
final Integer scale, final Boolean isUnicode) throws EdmPrimitiveTypeException {
final Calendar dateTimeValue;
final int fractionalSecs;
@ -197,11 +197,11 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
}
} catch (final IllegalArgumentException e) {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.VALUE_FACETS_NOT_MATCHED.addContent(value, facets)", e);
"EdmPrimitiveTypeException.VALUE_FACETS_NOT_MATCHED.addContent(value, facets)", e);
}
final int offsetInMinutes = (dateTimeValue.get(Calendar.ZONE_OFFSET)
+ dateTimeValue.get(Calendar.DST_OFFSET)) / 60 / 1000;
+ dateTimeValue.get(Calendar.DST_OFFSET)) / 60 / 1000;
final int offsetHours = offsetInMinutes / 60;
final int offsetMinutes = Math.abs(offsetInMinutes % 60);
final String offsetString = offsetInMinutes == 0 ? "Z" : String.format("%+03d:%02d", offsetHours, offsetMinutes);
@ -212,7 +212,7 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
/**
* Creates a date/time value from the given value.
*
*
* @param value the value as {@link Calendar}, {@link Date}, or {@link Long}
* @return the value as {@link Calendar}
* @throws EdmPrimitiveTypeException if the type of the value is not supported
@ -232,7 +232,7 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
dateTimeValue.setTimeInMillis((Long) value);
} else {
throw new EdmPrimitiveTypeException(
"EdmPrimitiveTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(value.getClass())");
"EdmPrimitiveTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(" + value.getClass().getName() + ")");
}
return dateTimeValue;
}
@ -240,7 +240,7 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
/**
* Appends the given number to the given string builder, assuming that the number has at most two digits,
* performance-optimized.
*
*
* @param result a {@link StringBuilder}
* @param number an integer that must satisfy <code>0 <= number <= 99</code>
*/
@ -252,14 +252,14 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
/**
* Appends the given number of milliseconds to the given string builder, assuming that the number has at most three
* digits, performance-optimized.
*
*
* @param result a {@link StringBuilder}
* @param milliseconds an integer that must satisfy <code>0 &lt;= milliseconds &lt;= 999</code>
* @param precision the upper limit for decimal digits (optional, defaults to zero)
* @throws IllegalArgumentException if precision is not met
*/
protected static void appendMilliseconds(final StringBuilder result, final int milliseconds,
final Integer precision) throws IllegalArgumentException {
final Integer precision) throws IllegalArgumentException {
final int digits = milliseconds % 1000 == 0 ? 0 : milliseconds % 100 == 0 ? 1 : milliseconds % 10 == 0 ? 2 : 3;
if (digits > 0) {
@ -279,14 +279,14 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
/**
* Appends the given fractional seconds to the given string builder.
*
*
* @param result a {@link StringBuilder}
* @param fractionalSeconds fractional seconds
* @param precision the upper limit for decimal digits (optional, defaults to zero)
* @throws IllegalArgumentException if precision is not met
*/
protected static void appendFractionalSeconds(final StringBuilder result, final int fractionalSeconds,
final Integer precision) throws IllegalArgumentException {
final Integer precision) throws IllegalArgumentException {
if (fractionalSeconds > 0) {
String formatted = NANO_FORMAT.get().format(fractionalSeconds);