/*
* Copyright (C) 2014 Civilian Framework.
*
* Licensed under the Civilian License (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.civilian-framework.org/license.txt
*
* 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.civilian.client;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import org.civilian.provider.PathParamProvider;
import org.civilian.resource.Path;
import org.civilian.resource.PathParam;
import org.civilian.resource.Route;
import org.civilian.response.UriEncoder;
import org.civilian.type.Type;
import org.civilian.type.TypeLib;
import org.civilian.type.fn.TypeSerializer;
import org.civilian.type.fn.StandardSerializer;
import org.civilian.util.Check;
/**
* WebUrl helps to build URLs to access a resource of a web application
* from a client program.
*/
public class WebUrl implements PathParamProvider
{
/**
* Creates a WebUrl consisting of the given URL string.
*/
public WebUrl(String url)
{
this(Route.constant(url));
}
/**
* Creates a WebUrl consisting of the route to the given resource, prefixed
* by the application URL.
*/
public WebUrl(WebResource resource)
{
this(resource.getRoute());
}
/**
* Creates a WebUrl consisting of the route to the given resource, prefixed
* by the application URL.
*/
public WebUrl(Route route)
{
route_ = Check.notNull(route, "route");
int ppCount = route_.getPathParamCount();
pathParams_ = ppCount > 0 ? new Object[ppCount] : EMPTY_PATH_PARAMS;
}
/**
* Appends a path snippet to the Url.
*/
public WebUrl addPath(String path)
{
if (additionalPath_ != null)
additionalPath_ = additionalPath_.add(path);
else
additionalPath_ = new Path(path);
return this;
}
//---------------------------------
// path parameters
//---------------------------------
/**
* Returns the number of path parameters within the Url.
* A positive count is only possible for Urls which have been
* constructed by passing a WebResource or Route object to the constructor.
*/
public int getPathParamCount()
{
return pathParams_.length;
}
/**
* Returns the i-th path param value.
*/
public Object getPathParam(int index)
{
return pathParams_[index];
}
/**
* Returns the path-param value for the given path parameter.
*/
@SuppressWarnings("unchecked")
@Override public <T> T getPathParam(PathParam<T> pathParam)
{
int index = route_.indexOf(pathParam);
return index >= 0 ? (T)pathParams_[index] : null;
}
/**
* Returns the i-th path-param object which defines the path parameter.
*/
public PathParam<?> getPathParamDef(int index)
{
return route_.getPathParam(index);
}
/**
* Sets the first path parameter value.
*/
public WebUrl setPathParam(Object value)
{
setPathParam(0, value);
return this;
}
/**
* Sets the i-th path parameter value.
*/
public WebUrl setPathParam(int index, Object value)
{
if ((index < 0) || (index >= pathParams_.length))
throw new NoSuchElementException(String.valueOf(index));
pathParams_[index] = value;
return this;
}
/**
* Sets the path parameters values.
*/
public WebUrl setPathParams(Object... values)
{
for (int i=0; i<values.length; i++)
setPathParam(i, values[i]);
return this;
}
/**
* Copies the path parameters from another url or
* PathParamProvider.
*/
public WebUrl copyPathParams(PathParamProvider provider)
{
Check.notNull(provider, "provider");
route_.extractPathParams(provider, pathParams_);
return this;
}
/**
* Sets the path parameter who is defined by the PathParam.
*/
public <T> WebUrl setPathParam(PathParam<T> param, T value)
{
int index = route_.indexOf(param);
if (index < 0)
throw new IllegalArgumentException("url does not contain a path parameter for " + param);
pathParams_[index] = value;
return this;
}
/**
* Sets the path parameter who is defined by the PathParam.
*/
public <T> WebUrl setPathParam(PathParam<Integer> param, int value)
{
return setPathParam(param, Integer.valueOf(value));
}
/**
* Clears all path parameters.
*/
public WebUrl clearPathParams()
{
for (int i=0; i<pathParams_.length; i++)
setPathParam(i, null);
return this;
}
//---------------------------------
// query parameters
//---------------------------------
/**
* Clears all query parameters of the Url.
*/
public WebUrl clearQueryParams()
{
if (params_ != null)
params_.clear();
return this;
}
/**
* Returns the number of query parameters.
*/
public int getQueryParamCount()
{
return params_ != null ? params_.size() : 0;
}
/**
* Returns the i-th query parameter.
*/
public QueryParam getQueryParam(int i)
{
return params_.get(i);
}
/**
* Returns the first query parameter with the given name.
* @param create if true and the url does not contain such a parameter
* a new parameter with that name is added.
*/
public QueryParam getQueryParam(String name, boolean create)
{
for (int i=0; i<getQueryParamCount(); i++)
{
if (params_.get(i).name_.equals(name))
return params_.get(i);
}
return create ? addQueryParam(name) : null;
}
/**
* Removes all query parameters with the specified name.
*/
public WebUrl removeQueryParam(String name)
{
for (int i=getQueryParamCount()-1; i>=0; i--)
{
if (params_.get(i).name_.equals(name))
params_.remove(i);
}
return this;
}
/**
* Removes all query parameters with the specified name.
*/
public WebUrl removeQueryParam(QueryParam param)
{
if (params_ != null)
params_.remove(param);
return this;
}
/**
* Adds a new query parameter to the Url.
* Use the setters on the returned Url to set the parameter value.
*/
public QueryParam addQueryParam(String name)
{
QueryParam param = new QueryParam(name);
if (params_ == null)
params_ = new ArrayList<>();
params_.add(param);
return param;
}
/**
* Adds a new parameter to the Url.
* @return this
*/
public WebUrl addEmptyQueryParam(String name)
{
addQueryParam(name);
return this;
}
/**
* Adds a query parameter with that name and value.
*/
public WebUrl addQueryParam(String name, String value)
{
addQueryParam(name).setValue(value);
return this;
}
/**
* Adds a query parameter with that name and value.
*/
public WebUrl addQueryParam(String name, int value)
{
addQueryParam(name).setValue(value);
return this;
}
/**
* Adds a query parameter with that name and value.
*/
public WebUrl addQueryParam(String name, Integer value)
{
addQueryParam(name).setValue(value);
return this;
}
/**
* Adds a query parameter with that name and value.
*/
public WebUrl addQueryParam(String name, boolean value)
{
addQueryParam(name).setValue(value);
return this;
}
/**
* Adds a query parameter with that name and value.
*/
public WebUrl addQueryParam(String name, Boolean value)
{
addQueryParam(name).setValue(value);
return this;
}
/**
* Adds a query parameter with that name, type and value.
*/
public <T> WebUrl addQueryParam(String name, Type<T> type, T value)
{
addQueryParam(name).setValue(type, value);
return this;
}
//---------------------------------
// serializer
//---------------------------------
/**
* Explicitly sets the TypeSerializer used by the Url when it
* formats typed parameters to a parameter string.
* @see QueryParam#setValue(Type, Object)
*/
public WebUrl setSerializer(TypeSerializer serializer)
{
serializer_ = Check.notNull(serializer, "serializer");
return this;
}
/**
* Returns the TypeSerializer used by the Url when it formats
* typed parameters to a parameter string.
*/
public TypeSerializer getSerializer()
{
return serializer_;
}
/**
* Sets the default TypeSerializer used by Urls.
*/
public void setDefaultTypeSerializer(TypeSerializer serializer)
{
defaultSerializer_ = Check.notNull(serializer, "serializer");
}
/**
* Returns the default TypeSerializer used by Urls.
*/
public static TypeSerializer getDefaultTypeSerializer()
{
return defaultSerializer_;
}
//---------------------------------
// fragment
//---------------------------------
/**
* Specifies the fragment which should be added to the end of the Url.
*/
public WebUrl setFragment(String fragment)
{
fragment_ = fragment;
return this;
}
/**
* Returns the fragment which will be added to the end of the Url.
*/
public String getFragment()
{
return fragment_;
}
//---------------------------------
// uriencoder
//---------------------------------
/**
* Sets the UriEncoder which should be used to encode parameters.
*/
public WebUrl setUriEncoder(UriEncoder uriEncoder)
{
uriEncoder_ = uriEncoder;
return this;
}
/**
* Returns the UriEncoder which should be used to encode parameters.
*/
public UriEncoder getUriEncoder()
{
if (uriEncoder_ == null)
uriEncoder_ = new UriEncoder();
return uriEncoder_;
}
//---------------------------------
// inner classes
//---------------------------------
/**
* QueryParam models a URL query parameter.
*/
public class QueryParam
{
/**
* Creates a new QueryParam.
* @param name the name
*/
public QueryParam(String name)
{
name_ = Check.notNull(name, "name");
}
/**
* Returns the query parameter name.
*/
public String getName()
{
return name_;
}
/**
* Returns the query parameter value.
*/
public String getValue()
{
return value_;
}
/**
* Set the value of the query parameter to a string.
*/
public void setValue(String value)
{
value_ = value;
}
/**
* Sets the int value of the query parameter.
*/
public void setValue(int value)
{
setValue(Integer.valueOf(value));
}
/**
* Sets the Integer value of the query parameter.
*/
public void setValue(Integer value)
{
setValue(TypeLib.INTEGER, value);
}
/**
* Sets the boolean value of the query parameter.
*/
public void setValue(boolean value)
{
setValue(TypeLib.BOOLEAN, Boolean.valueOf(value));
}
/**
* Sets the boolean value of the query parameter.
*/
public void setValue(Boolean value)
{
setValue(TypeLib.BOOLEAN, value);
}
/**
* Sets the value of the query parameter.
* The Urls type serializer is used to convert the value to a string.
* @see WebUrl#getSerializer()
*/
public <T> void setValue(Type<T> type, T value)
{
value_ = getSerializer().format(type, value);
}
void append(UriEncoder encoder, StringBuilder s)
{
encoder.encode(name_, s);
s.append('=');
if (value_ != null)
encoder.encode(value_, s);
}
private String name_;
private String value_;
}
//---------------------------------
// serialize
//---------------------------------
/**
* Converts the WebUrl to a string.
*/
@Override public String toString()
{
StringBuilder s = new StringBuilder();
route_.build(pathParams_, getUriEncoder(), s);
if (additionalPath_ != null)
additionalPath_.addTo(s);
if (params_ != null)
{
int n = params_.size();
for (int i=0; i<n; i++)
{
s.append(i == 0 ? '?' : '&');
params_.get(i).append(getUriEncoder(), s);
}
}
if (fragment_ != null)
{
s.append('#');
s.append(fragment_);
}
return s.toString();
}
private UriEncoder uriEncoder_;
private String fragment_;
private Route route_;
private Path additionalPath_;
private Object[] pathParams_;
private ArrayList<QueryParam> params_;
private TypeSerializer serializer_ = defaultSerializer_;
private static TypeSerializer defaultSerializer_ = StandardSerializer.INSTANCE;
private static final Object[] EMPTY_PATH_PARAMS = new Object[0];
}