package org.odata4j.consumer; import java.io.Reader; import java.math.BigDecimal; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.core4j.Enumerable; import org.core4j.Func; import org.core4j.ReadOnlyIterator; import org.joda.time.LocalDateTime; import org.odata4j.core.Guid; import org.odata4j.core.OCollection; import org.odata4j.core.ODataConstants; import org.odata4j.core.ODataHttpMethod; import org.odata4j.core.ODataVersion; import org.odata4j.core.OEntity; import org.odata4j.core.OEntityKey; import org.odata4j.core.OFunctionParameter; import org.odata4j.core.OFunctionParameters; import org.odata4j.core.OFunctionRequest; import org.odata4j.core.OObject; import org.odata4j.core.OSimpleObject; import org.odata4j.core.OSimpleObjects; import org.odata4j.core.UnsignedByte; import org.odata4j.edm.EdmCollectionType; import org.odata4j.edm.EdmDataServices; import org.odata4j.edm.EdmEntitySet; import org.odata4j.edm.EdmFunctionImport; import org.odata4j.edm.EdmProperty.CollectionKind; import org.odata4j.edm.EdmSimpleType; import org.odata4j.edm.EdmType; import org.odata4j.exceptions.ODataProducerException; import org.odata4j.expression.Expression; import org.odata4j.expression.LiteralExpression; import org.odata4j.format.FormatParser; import org.odata4j.format.FormatParserFactory; import org.odata4j.format.Parameters; import org.odata4j.format.FormatType; import org.odata4j.format.Settings; import org.odata4j.internal.EntitySegment; import org.odata4j.internal.InternalUtil; /** * Function-call-request implementation. */ public class ConsumerFunctionCallRequest<T extends OObject> extends AbstractConsumerQueryRequestBase<T> implements OFunctionRequest<T> { private final List<OFunctionParameter> params = new LinkedList<OFunctionParameter>(); private final String functionName; private String boundEntitySetName; private OEntityKey boundEntityKey; private EdmFunctionImport function; public ConsumerFunctionCallRequest(ODataClient client, String serviceRootUri, EdmDataServices metadata, String lastSegment) { super(client, serviceRootUri, metadata, lastSegment, true); // lastSegment is the function call name. this.functionName = lastSegment; if (!metadata.containsEdmFunctionImport(lastSegment)) throw new IllegalArgumentException("No Function Import named '" + lastSegment + "' defined"); } @SuppressWarnings("unchecked") @Override public Enumerable<T> execute() throws ODataProducerException { final ODataClientRequest request = buildFunctionRequest(); Enumerable<OObject> results; if (function.getReturnType() == null) { //doRequest(request); getClient().callFunction(request); results = Enumerable.empty(null); } else if (function.getReturnType() instanceof EdmCollectionType) { final OCollection<OObject> collection = (OCollection<OObject>) doRequest(request); results = Enumerable.createFromIterator( new Func<Iterator<OObject>>() { @Override public Iterator<OObject> apply() { return new FunctionResultsIterator(request, collection); } }); } else { results = Enumerable.create(doRequest(request)); } return (Enumerable<T>) results; } private ODataClientRequest buildFunctionRequest() { EdmType bindingType = null; if (boundEntitySetName != null) { EdmEntitySet entitySet = getMetadata().findEdmEntitySet(boundEntitySetName); if (entitySet != null){ bindingType = entitySet.getType(); if (boundEntityKey == null) { // The binding type is a collection as we don't have the entity key bindingType = new EdmCollectionType(CollectionKind.Collection, bindingType); } } List<EntitySegment> entitySegments = getSegments(); entitySegments.add(0, new EntitySegment(boundEntitySetName, boundEntityKey)); } function = getMetadata().findEdmFunctionImport(functionName, bindingType); if (function == null) { throw new IllegalArgumentException("No function found matching your request"); } if (function.getHttpMethod().equalsIgnoreCase(ODataHttpMethod.GET.name()) || function.getHttpMethod().equalsIgnoreCase(ODataHttpMethod.DELETE.name())){ // turn each param into a custom query option for (OFunctionParameter p : params) custom(p.getName(), toUriString(p)); return buildRequest(null); } else { ODataClientRequest request = buildRequest(null); request = request.method(function.getHttpMethod()); request = request.payload(new Parameters() { @Override public Collection<OFunctionParameter> getParameters() { return params; } }); return request; } } private static String toUriString(OFunctionParameter p) { OObject obj = p.getValue(); if (obj instanceof OSimpleObject) { OSimpleObject<?> simple = (OSimpleObject<?>) obj; LiteralExpression le = Expression.literal(simple.getType(), simple.getValue()); return Expression.asFilterString(le); } else if (obj instanceof OEntity) { OEntity entity = (OEntity)obj; OEntityKey key = entity.getEntityKey(); if (key == null){ throw new UnsupportedOperationException("Locally-built type not supported (No entity key): " + obj.getType().getFullyQualifiedTypeName()); } return entity.getEntitySetName() + key.toKeyString(); } throw new UnsupportedOperationException("type not supported: " + obj.getType().getFullyQualifiedTypeName()); } // set parameters to the function call @Override public ConsumerFunctionCallRequest<T> parameter(String name, OObject value) { params.add(OFunctionParameters.create(name, value)); return this; } @Override public OFunctionRequest<T> pBoolean(String name, boolean value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.BOOLEAN, value)); } @Override public OFunctionRequest<T> pByte(String name, UnsignedByte value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.BYTE, value)); } @Override public OFunctionRequest<T> pSByte(String name, byte value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.SBYTE, value)); } @Override public OFunctionRequest<T> pDateTime(String name, Calendar value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.DATETIME, value)); } @Override public OFunctionRequest<T> pDateTime(String name, Date value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.DATETIME, value)); } @Override public OFunctionRequest<T> pDateTime(String name, LocalDateTime value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.DATETIME, value)); } @Override public OFunctionRequest<T> pDecimal(String name, BigDecimal value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.DECIMAL, value)); } @Override public OFunctionRequest<T> pDouble(String name, double value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.DOUBLE, value)); } @Override public OFunctionRequest<T> pGuid(String name, Guid value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.GUID, value)); } @Override public OFunctionRequest<T> pInt16(String name, short value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.INT16, value)); } @Override public OFunctionRequest<T> pInt32(String name, int value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.INT32, value)); } @Override public OFunctionRequest<T> pInt64(String name, long value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.INT64, value)); } @Override public OFunctionRequest<T> pSingle(String name, float value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.SINGLE, value)); } @Override public OFunctionRequest<T> pTime(String name, Calendar value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.TIME, value)); } @Override public OFunctionRequest<T> pTime(String name, Date value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.TIME, value)); } @Override public OFunctionRequest<T> pTime(String name, LocalDateTime value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.TIME, value)); } @Override public OFunctionRequest<T> pString(String name, String value) { return parameter(name, OSimpleObjects.create(EdmSimpleType.STRING, value)); } @Override public OFunctionRequest<T> bind(String entitySetName) { this.boundEntitySetName = entitySetName; return this; } @Override public OFunctionRequest<T> bind(String entitySetName, OEntityKey key) { this.boundEntitySetName = entitySetName; this.boundEntityKey = key; return this; } private OObject doRequest(ODataClientRequest request) throws ODataProducerException { ODataClientResponse response = getClient().callFunction(request); return (OObject) getResult(response); } private class FunctionResultsIterator extends ReadOnlyIterator<OObject> { private ODataClientRequest request; private OCollection<OObject> current = null; private Iterator<OObject> iter = null; private int count = 0; public FunctionResultsIterator(ODataClientRequest request, OCollection<OObject> current) { this.request = request; this.current = current; this.iter = current.iterator(); } @SuppressWarnings("unchecked") @Override protected IterationResult<OObject> advance() throws Exception { if (current == null) { current = (OCollection<OObject>) doRequest(this.request); iter = current.iterator(); count = 0; } if (iter != null && iter.hasNext()) { count++; return IterationResult.next(iter.next()); } else { return IterationResult.done(); } /* TODO support paging */ } } private ODataClientRequest getRequest() { return buildFunctionRequest(); } private Object getResult(ODataClientResponse response) { ODataVersion version = InternalUtil.getDataServiceVersion(response.getHeaders().getFirst(ODataConstants.Headers.DATA_SERVICE_VERSION)); Object object = getResult(version, getClient().getFeedReader(response), getClient().getFormatType()); response.close(); return object; } private Object getResult(ODataVersion version, Reader reader, FormatType formatType) { if (function.getReturnType() == null) { return null; } FormatParser<? extends OObject> parser = FormatParserFactory.getParser( function.getReturnType().isSimple() ? OSimpleObject.class : EdmType.getInstanceType(function.getReturnType()), getClient().getFormatType(), new Settings(version, getMetadata(), function.getEntitySet() != null ? function.getEntitySet().getName() : null, null, // entitykey null, // fcMapping true, // isResponse function.getReturnType(), function) ); OObject object = parser.parse(reader); return object; } @Override public String formatRequest(FormatType formatType) { ODataClientRequest request = getRequest(); return ConsumerBatchRequestHelper.formatSingleRequest(request, formatType); } @Override public Object getResult(ODataVersion version, Object payload, FormatType formatType) { Reader reader = getClient().getFeedReader((String) payload); return getResult(version, reader, formatType); } }