Merge remote-tracking branch 'origin/master' into OLINGO-377-FIT
This commit is contained in:
commit
3aab1c1276
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <= milliseconds <= 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);
|
||||
|
|
Loading…
Reference in New Issue