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

Java tutorial

Introduction

Here is the source code for org.apache.olingo.client.core.uri.AbstractURIBuilder.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 org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.apache.olingo.client.api.Configuration;
import org.apache.olingo.client.api.uri.CommonURIBuilder;
import org.apache.olingo.client.api.uri.QueryOption;
import org.apache.olingo.client.api.uri.SegmentType;
import org.apache.olingo.client.api.uri.URIFilter;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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;

public abstract class AbstractURIBuilder<UB extends CommonURIBuilder<?>> implements CommonURIBuilder<UB> {

    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(CommonURIBuilder.class);

    private final ODataServiceVersion version;

    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>();

    /**
     * Constructor.
     *
     * @param serviceRoot absolute URL (schema, host and port included) representing the location of the root of the data
     * service.
     */
    protected AbstractURIBuilder(final ODataServiceVersion version, final Configuration configuration,
            final String serviceRoot) {

        this.version = version;
        this.configuration = configuration;
        segments.add(new Segment(SegmentType.SERVICEROOT, serviceRoot));
    }

    protected abstract UB getThis();

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

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

    @Override
    public UB 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 getThis();
    }

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

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

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

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

        return getThis();
    }

    protected abstract String noKeysWrapper();

    @Override
    public UB 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 getThis();
    }

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

    }

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

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

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

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

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

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

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

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

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

    @Override
    public UB filter(final URIFilter filter) {
        UB 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 UB filter(final String filter) {
        return replaceQueryOption(QueryOption.FILTER, filter);
    }

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

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

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

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

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

    protected abstract char getBoundOperationSeparator();

    protected abstract String getOperationInvokeMarker();

    @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;

                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 {
            StringBuilder sb = segmentsBuilder;
            if ((queryOptions.size() + parameters.size()) > 0) {
                sb.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).
                String queryStr = URLEncodedUtils.format(list1, "UTF-8");
                sb.append(queryStr);
            }

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

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

    protected String buildMultiKeySegment(final Map<String, Object> segmentValues, final boolean escape) {
        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(version, entry.getValue()) : entry.getValue());
                keyBuilder.append(',');
            }
            keyBuilder.deleteCharAt(keyBuilder.length() - 1).append(')');

            return keyBuilder.toString();
        }
    }
}