org.apache.olingo.client.core.uri.URIBuilderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.olingo.client.core.uri.URIBuilderImpl.java

Source

/*
 * 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.olingo.client.core.uri;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.olingo.client.api.Configuration;
import org.apache.olingo.client.api.uri.QueryOption;
import org.apache.olingo.client.api.uri.SegmentType;
import org.apache.olingo.client.api.uri.URIBuilder;
import org.apache.olingo.client.api.uri.URIFilter;
import org.apache.olingo.client.api.uri.URISearch;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.core.Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class URIBuilderImpl implements URIBuilder {

    /**
     * Constructor.
     *
     * @param serviceRoot absolute URL (schema, host and port included) representing the location of the root of the data
     * service.
     */
    public URIBuilderImpl(final Configuration configuration, final String serviceRoot) {
        this.configuration = configuration;

        segments.add(new Segment(SegmentType.SERVICEROOT, serviceRoot));
    }

    protected static class Segment {

        private final SegmentType type;

        private final String value;

        public Segment(final SegmentType type, final String value) {
            this.type = type;
            this.value = value;
        }

        public SegmentType getType() {
            return type;
        }

        public String getValue() {
            return value;
        }
    }

    /**
     * Logger.
     */
    protected static final Logger LOG = LoggerFactory.getLogger(URIBuilderImpl.class);

    private final Configuration configuration;

    protected final List<Segment> segments = new ArrayList<Segment>();

    /**
     * Insertion-order map of query options.
     */
    protected final Map<String, String> queryOptions = new LinkedHashMap<String, String>();

    /**
     * Insertion-order map of parameter aliases.
     */
    protected final Map<String, String> parameters = new LinkedHashMap<String, String>();

    @Override
    public URIBuilder addQueryOption(final QueryOption option, final String value) {
        return addQueryOption(option.toString(), value, false);
    }

    @Override
    public URIBuilder replaceQueryOption(final QueryOption option, final String value) {
        return addQueryOption(option.toString(), value, true);
    }

    @Override
    public URIBuilder addQueryOption(final String option, final String value, final boolean replace) {
        final StringBuilder builder = new StringBuilder();
        if (!replace && queryOptions.containsKey(option)) {
            builder.append(queryOptions.get(option)).append(',');
        }
        builder.append(value);
        queryOptions.put(option, builder.toString());
        return this;
    }

    @Override
    public URIBuilder addParameterAlias(final String alias, final String exp) {
        parameters.put(alias, exp);
        return this;
    }

    @Override
    public URIBuilder appendEntitySetSegment(final String segmentValue) {
        segments.add(new Segment(SegmentType.ENTITYSET, segmentValue));
        return this;
    }

    @Override
    public URIBuilder appendKeySegment(final Object val) {
        final String segValue = URIUtils.escape(val);

        segments.add(configuration.isKeyAsSegment() ? new Segment(SegmentType.KEY_AS_SEGMENT, segValue)
                : new Segment(SegmentType.KEY, "(" + segValue + ")"));

        return this;
    }

    @Override
    public URIBuilder appendKeySegment(final Map<String, Object> segmentValues) {
        if (!configuration.isKeyAsSegment()) {
            final String key = buildMultiKeySegment(segmentValues, true, ',');
            if (StringUtils.isEmpty(key)) {
                segments.add(new Segment(SegmentType.KEY, noKeysWrapper()));
            } else {
                segments.add(new Segment(SegmentType.KEY, key));
            }
        }

        return this;
    }

    @Override
    public URIBuilder appendPropertySegment(final String segmentValue) {
        segments.add(new Segment(SegmentType.PROPERTY, segmentValue));
        return this;

    }

    @Override
    public URIBuilder appendNavigationSegment(final String segmentValue) {
        segments.add(new Segment(SegmentType.NAVIGATION, segmentValue));
        return this;
    }

    @Override
    public URIBuilder appendDerivedEntityTypeSegment(final String segmentValue) {
        segments.add(new Segment(SegmentType.DERIVED_ENTITY_TYPE, segmentValue));
        return this;
    }

    @Override
    public URIBuilder appendValueSegment() {
        segments.add(new Segment(SegmentType.VALUE, SegmentType.VALUE.getValue()));
        return this;
    }

    @Override
    public URIBuilder appendCountSegment() {
        segments.add(new Segment(SegmentType.COUNT, SegmentType.COUNT.getValue()));
        return this;
    }

    @Override
    public URIBuilder appendActionCallSegment(final String action) {
        segments.add(
                new Segment(segments.size() == 1 ? SegmentType.UNBOUND_ACTION : SegmentType.BOUND_ACTION, action));
        return this;
    }

    @Override
    public URIBuilder appendOperationCallSegment(final String operation) {
        segments.add(new Segment(segments.size() == 1 ? SegmentType.UNBOUND_OPERATION : SegmentType.BOUND_OPERATION,
                operation));
        return this;
    }

    @Override
    public URIBuilder appendMetadataSegment() {
        segments.add(new Segment(SegmentType.METADATA, SegmentType.METADATA.getValue()));
        return this;
    }

    @Override
    public URIBuilder appendBatchSegment() {
        segments.add(new Segment(SegmentType.BATCH, SegmentType.BATCH.getValue()));
        return this;
    }

    @Override
    public URIBuilder count() {
        segments.add(new Segment(SegmentType.ROOT_QUERY_OPTION, "$" + QueryOption.COUNT.toString()));
        return this;
    }

    @Override
    public URIBuilder expand(final String... expandItems) {
        return addQueryOption(QueryOption.EXPAND, StringUtils.join(expandItems, ","));
    }

    @Override
    public URIBuilder format(final String format) {
        return replaceQueryOption(QueryOption.FORMAT, format);
    }

    @Override
    public URIBuilder filter(final URIFilter filter) {
        URIBuilder result;
        try {
            // decode in order to support @ in parameter aliases
            result = filter(URLDecoder.decode(filter.build(), Constants.UTF8));
        } catch (UnsupportedEncodingException e) {
            result = filter(filter.build());
        }
        return result;
    }

    @Override
    public URIBuilder filter(final String filter) {
        return replaceQueryOption(QueryOption.FILTER, filter);
    }

    @Override
    public URIBuilder select(final String... selectItems) {
        return addQueryOption(QueryOption.SELECT, StringUtils.join(selectItems, ","));
    }

    @Override
    public URIBuilder orderBy(final String order) {
        return replaceQueryOption(QueryOption.ORDERBY, order);
    }

    @Override
    public URIBuilder top(final int top) {
        return replaceQueryOption(QueryOption.TOP, String.valueOf(top));
    }

    @Override
    public URIBuilder skip(final int skip) {
        return replaceQueryOption(QueryOption.SKIP, String.valueOf(skip));
    }

    @Override
    public URIBuilder skipToken(final String skipToken) {
        return replaceQueryOption(QueryOption.SKIPTOKEN, skipToken);
    }

    @Override
    public URI build() {
        final StringBuilder segmentsBuilder = new StringBuilder();

        for (Segment seg : segments) {
            if (segmentsBuilder.length() > 0 && seg.getType() != SegmentType.KEY) {
                switch (seg.getType()) {
                case BOUND_OPERATION:
                    segmentsBuilder.append(getBoundOperationSeparator());
                    break;
                case BOUND_ACTION:
                    segmentsBuilder.append(getBoundOperationSeparator());
                    break;
                default:
                    if (segmentsBuilder.length() > 0
                            && segmentsBuilder.charAt(segmentsBuilder.length() - 1) != '/') {
                        segmentsBuilder.append('/');
                    }
                }
            }

            if (seg.getType() == SegmentType.ENTITY) {
                segmentsBuilder.append(seg.getType().getValue());
            } else {
                segmentsBuilder.append(seg.getValue());
            }
            if (seg.getType() == SegmentType.BOUND_OPERATION || seg.getType() == SegmentType.UNBOUND_OPERATION) {
                segmentsBuilder.append(getOperationInvokeMarker());
            }
        }

        try {
            if ((queryOptions.size() + parameters.size()) > 0) {
                segmentsBuilder.append("?");
                List<NameValuePair> list1 = new LinkedList<NameValuePair>();
                for (Map.Entry<String, String> option : queryOptions.entrySet()) {
                    list1.add(new BasicNameValuePair("$" + option.getKey(), option.getValue()));
                }
                for (Map.Entry<String, String> parameter : parameters.entrySet()) {
                    list1.add(new BasicNameValuePair("@" + parameter.getKey(), parameter.getValue()));
                }

                // don't use UriBuilder.build():
                // it will try to call URLEncodedUtils.format(Iterable<>,Charset) method,
                // which works in desktop java application, however, throws NoSuchMethodError in android OS,
                // so here manually construct the URL by its overload URLEncodedUtils.format(List<>,String).
                final String queryStr = encodeQueryParameter(list1);
                segmentsBuilder.append(queryStr);
            }

            return URI.create(segmentsBuilder.toString());
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Could not build valid URI", e);
        }
    }

    private String encodeQueryParameter(List<NameValuePair> list) {
        final StringBuilder builder = new StringBuilder();

        for (NameValuePair pair : list) {
            if (builder.length() > 0) {
                builder.append("&");
            }

            builder.append(Encoder.encode(pair.getName()));
            builder.append("=");
            builder.append(Encoder.encode(pair.getValue()));
        }

        return builder.toString();
    }

    @Override
    public String toString() {
        return build().toASCIIString();
    }

    protected String buildMultiKeySegment(final Map<String, Object> segmentValues, final boolean escape,
            final char sperator) {
        if (segmentValues == null || segmentValues.isEmpty()) {
            return StringUtils.EMPTY;
        } else {
            final StringBuilder keyBuilder = new StringBuilder().append('(');
            for (Map.Entry<String, Object> entry : segmentValues.entrySet()) {
                keyBuilder.append(entry.getKey()).append('=')
                        .append(escape ? URIUtils.escape(entry.getValue()) : entry.getValue());
                keyBuilder.append(sperator);
            }
            keyBuilder.deleteCharAt(keyBuilder.length() - 1).append(')');

            return keyBuilder.toString();
        }
    }

    @Override
    public URIBuilder appendKeySegment(final EdmEnumType enumType, final String memberName) {
        return appendKeySegment(enumType.toUriLiteral(memberName));
    }

    @Override
    public URIBuilder appendKeySegment(final Map<String, Pair<EdmEnumType, String>> enumValues,
            final Map<String, Object> segmentValues) {

        final Map<String, Object> values = new LinkedHashMap<String, Object>();
        for (Map.Entry<String, Pair<EdmEnumType, String>> entry : enumValues.entrySet()) {
            values.put(entry.getKey(), entry.getValue().getKey().toUriLiteral(entry.getValue().getValue()));
        }
        values.putAll(segmentValues);

        return appendKeySegment(values);
    }

    protected String noKeysWrapper() {
        return "()";
    }

    protected char getBoundOperationSeparator() {
        return '/';
    }

    protected String getOperationInvokeMarker() {
        return "()";
    }

    @Override
    public URIBuilder appendSingletonSegment(final String segmentValue) {
        segments.add(new Segment(SegmentType.SINGLETON, segmentValue));
        return this;
    }

    @Override
    public URIBuilder appendEntityIdSegment(final String segmentValue) {
        segments.add(new Segment(SegmentType.ENTITY, null));
        return addQueryOption(QueryOption.ID, segmentValue);
    }

    @Override
    public URIBuilder appendRefSegment() {
        segments.add(new Segment(SegmentType.REF, SegmentType.REF.getValue()));
        return this;
    }

    @Override
    public URIBuilder appendCrossjoinSegment(final String... segmentValues) {
        final StringBuilder segValue = new StringBuilder(SegmentType.CROSS_JOIN.getValue()).append('(')
                .append(StringUtils.join(segmentValues, ",")).append(')');
        segments.add(new Segment(SegmentType.CROSS_JOIN, segValue.toString()));
        return this;
    }

    @Override
    public URIBuilder appendAllSegment() {
        segments.add(new Segment(SegmentType.ALL, SegmentType.ALL.getValue()));
        return this;
    }

    @Override
    public URIBuilder id(final String idValue) {
        return addQueryOption(QueryOption.ID, idValue);
    }

    @Override
    public URIBuilder search(final URISearch search) {
        return search(search.build());
    }

    @Override
    public URIBuilder search(final String expression) {
        return addQueryOption(QueryOption.SEARCH, expression);
    }

    @Override
    public URIBuilder count(final boolean value) {
        return addQueryOption(QueryOption.COUNT, Boolean.toString(value));
    }

    @Override
    public URIBuilder expandWithOptions(final String expandItem, final Map<QueryOption, Object> options) {
        return expandWithOptions(expandItem, false, false, options);
    }

    @Override
    public URIBuilder expandWithOptions(String expandItem, boolean pathRef, boolean pathCount,
            Map<QueryOption, Object> options) {
        final Map<String, Object> _options = new LinkedHashMap<String, Object>();
        for (Map.Entry<QueryOption, Object> entry : options.entrySet()) {
            _options.put("$" + entry.getKey().toString(), entry.getValue());
        }
        String path = pathRef ? "/$ref" : pathCount ? "/$count" : StringUtils.EMPTY;
        return expand(expandItem + buildMultiKeySegment(_options, false, ';') + path);
    }

    @Override
    public URIBuilder expandWithSelect(final String expandItem, final String... selectItems) {
        return expand(expandItem + "($select=" + StringUtils.join(selectItems, ",") + ")");
    }
}