package com.rackspacecloud.client.service_registry.curator;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

class Utils {
    public static final int MAX_TAG_LENGTH = 55;

    // 1..55 chars. anything goes.
    public static String sanitizeTag(String s) {
        // if > 55 chars, assume max entropy is at the end (like a class name).
        if (s.length() > MAX_TAG_LENGTH) {
            s = s.substring(s.length() - MAX_TAG_LENGTH);
        }
        return s;
    }

    public static Collection<Field> getMetaFields(Class cls) {
        List<Field> allFields = new ArrayList<Field>();
        List<Field> metaFields = new ArrayList<Field>();
        for (Field f : cls.getDeclaredFields())
            allFields.add(f);
        for (Field f : cls.getFields())
            allFields.add(f);
        for (Field f : allFields) {
            for (Annotation a : f.getAnnotations()) {
                if (a.annotationType().equals(Meta.class)) {
                    metaFields.add(f);
                }
            }
        }
        return metaFields;
    }
}


package com.github.kmbulebu.nicknack.server.web;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.Collection;
import java.util.UUID;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.Resources;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.github.kmbulebu.nicknack.server.Application;
import com.github.kmbulebu.nicknack.server.model.StateDefinitionResource;
import com.github.kmbulebu.nicknack.server.model.StatesResource;
import com.github.kmbulebu.nicknack.server.services.StatesService;
import com.github.kmbulebu.nicknack.server.services.exceptions.AttributeDefinitionNotFoundException;
import com.github.kmbulebu.nicknack.server.services.exceptions.ProviderNotFoundException;
import com.github.kmbulebu.nicknack.server.services.exceptions.StateDefinitionNotFoundException;

@RestController
@RequestMapping(value="/api/states", produces={"application/hal+json"})
@ExposesResourceFor(StatesResource.class)
public class StatesController {

	private static final Logger LOG = LogManager.getLogger(Application.APP_LOGGER_NAME);

	@Autowired
	private StatesService statesService;

	@Autowired
	private RelProvider relProvider;

	@Autowired
	private EntityLinks entityLinks;

	// TODO Refactor HATEOAS link building. Time for ResourceAssembler or moving this stuff up the service. Although moving it up will create a circular dep.

	@RequestMapping(value="", method={RequestMethod.GET, RequestMethod.HEAD})
	public Resources<StatesResource> getAllStates() throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry();
		}

		final Resources<StatesResource> resources = getAllStates(statesService.getAllStates(), null);

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

	@RequestMapping(value="", params="provider", method={RequestMethod.GET, RequestMethod.HEAD})
	public Resources<StatesResource> getAllStates(@RequestParam UUID provider) throws ProviderNotFoundException, StateDefinitionNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(provider);
		}

		final Resources<StatesResource> resources = getAllStates(statesService.getAllStatesByProvider(provider), provider);

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

	@RequestMapping(value="/{uuid}", method={RequestMethod.GET, RequestMethod.HEAD})
	public StatesResource getStates(@PathVariable UUID uuid) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(uuid);
		}

		final StatesResource resource = statesService.getStates(uuid);

		resource.add(entityLinks.linkToSingleResource(StatesResource.class, uuid).withSelfRel());
		resource.add(linkTo(methodOn(StateDefinitionsController.class).getStateDefinition(resource.getStateDefinition().getUUID())).withRel(relProvider.getItemResourceRelFor(StateDefinitionResource.class)));

		if (LOG.isTraceEnabled()) {
			LOG.exit(resource);
		}
		return resource;
	}

	private Resources<StatesResource> getAllStates(Collection<StatesResource> statesResources, UUID provider) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(statesResources);
		}

		for (StatesResource resource : statesResources) {
			resource.add(entityLinks.linkToSingleResource(StatesResource.class, resource.getStateDefinition().getUUID()).withSelfRel());
			resource.add(linkTo(methodOn(StateDefinitionsController.class).getStateDefinition(resource.getStateDefinition().getUUID())).withRel(relProvider.getItemResourceRelFor(StateDefinitionResource.class)));
		}

		final Resources<StatesResource> resources = new Resources<>(statesResources);
		if (provider == null) {
			resources.add(entityLinks.linkToCollectionResource(StatesResource.class));
		} else {
			resources.add(linkTo(methodOn(StatesController.class).getAllStates(provider)).withSelfRel());
		}

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

}


package com.bytebybyte.google.geocoding.service.request;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

public class GeocodeRequestBuilder {

	protected Map<String, String> parameters = new LinkedHashMap<String, String>();

	public GeocodeRequestBuilder() {
	}

	/**
	 * Your 'free' application's API key. This key identifies your application
	 * for purposes of quota management.
	 *
	 * @param key
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder key(String key) {
		parameters.put("key", key);
		return this;
	}

	/**
	 * Your 'business' application's API key. This key identifies your
	 * application for purposes of quota management.
	 *
	 * @param client
	 * @param signature
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder key(String client, String signature) {
		parameters.put("client", client);
		parameters.put("signature", signature);
		return this;
	}

	/**
	 * Output format.
	 *
	 * - json (recommended) indicates output in JavaScript Object Notation.
	 *
	 * - xml indicates output as XML
	 *
	 * @param output
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder output(String output) {
		parameters.put("output", output);
		return this;
	}

	/**
	 * The address that you want to geocode.
	 *
	 * @param address
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder address(String address) {
		parameters.put("address", address != null ? address.replace(' ', '+')
				: address);
		return this;
	}

	/**
	 * In a geocoding response, the Google Geocoding API can return address
	 * results restricted to a specific area.
	 *
	 * The restriction is specified using the components filter.
	 *
	 * A filter consists of a list of component:value pairs separated by a pipe
	 * (|). Only the results that match all the filters will be returned.
	 *
	 * Filter values support the same methods of spelling correction and partial
	 * matching as other geocoding requests. If a geocoding result is a partial
	 * match for a component filter it will contain a partial_match field in the
	 * response.
	 *
	 * The components that can be filtered include:
	 *
	 * route: matches long or short name of a route.
	 *
	 * locality: matches against both locality and sublocality types.
	 *
	 * administrative_area: matches all the administrative_area levels.
	 *
	 * postal_code: matches postal_code and postal_code_prefix.
	 *
	 * country: matches a country name or a two letter ISO 3166-1 country code.
	 *
	 * Note: Each address component can only be specified either in the address
	 * parameter or as a component filter, but not both. Doing so may result in
	 * ZERO_RESULTS.
	 *
	 * Countries and administrative areas of level 1 are exempted from this.
	 *
	 * @param components
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder componenets(Map<String, String> components) {
		StringBuffer filters = new StringBuffer();
		for (Iterator<Map.Entry<String, String>> iterator = components
				.entrySet().iterator(); iterator.hasNext();) {
			Map.Entry<String, String> entry = iterator.next();
			filters.append(entry.getKey() + ":" + entry.getValue() != null ? entry
					.getValue().replace(' ', '+') : entry.getValue());
			if (iterator.hasNext())
				filters.append("|");
		}
		parameters.put("components", filters.toString());
		return this;
	}

	/**
	 * The bounding box of the viewport within which to bias geocode results
	 * more prominently. This parameter will only influence, not fully restrict,
	 * results from the geocoder.
	 *
	 * @param bounds
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder bounds(String bounds) {
		parameters.put("bounds", bounds);
		return this;
	}

	/**
	 * The language in which to return results. If language is not supplied, the
	 * geocoder will attempt to use the native language of the domain from which
	 * the request is sent wherever possible.
	 *
	 * @param language
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder language(String language) {
		parameters.put("language", language);
		return this;
	}

	/**
	 * TThe region code, specified as a ccTLD ("top-level domain") two-character
	 * value. This parameter will only influence, not fully restrict, results
	 * from the geocoder.
	 *
	 * @param region
	 * @return GeocodeRequestBuilder
	 */
	public GeocodeRequestBuilder region(String region) {
		parameters.put("region", region);
		return this;
	}

	/**
	 * Build the request object.
	 *
	 * @return GeocodeRequest
	 */
	public GeocodeRequest build() {
		return new GeocodeRequest(parameters);
	}
}


package com.rackspacecloud.client.service_registry.examples;

import com.netflix.curator.x.discovery.ServiceInstance;
import com.netflix.curator.x.discovery.ServiceInstanceBuilder;
import com.netflix.curator.x.discovery.ServiceType;
import com.netflix.curator.x.discovery.UriSpec;
import com.rackspacecloud.client.service_registry.curator.Meta;
import com.rackspacecloud.client.service_registry.objects.Service;

public class BarCuratorService {
    // all fields annotated with @Meta will be seen as metadata in the RSR service instance.
    @Meta public String name;
    @Meta public String a;
    @Meta public int b;
    @Meta public float c;

    // this constructor gets used by one of the convert methods (after a service is read from RSR).
    public BarCuratorService(String name, String a, int b, float c) {
        this.name = name;
        this.a = a;
        this.b = b;
        this.c = c;
    }

    // one of the required convert methods.  This one converts instances of the users service class into
    // ServiceInstance<T> instances required by the Curator interfaces.
    public static ServiceInstance<BarCuratorService> convert(BarCuratorService bar) throws Exception {
        ServiceInstanceBuilder<BarCuratorService> builder = ServiceInstance.builder();
        // these values are for the most part nonsensical.
        return builder.payload(bar)
            .uriSpec(new UriSpec("http://"))
            .sslPort(2400)
            .serviceType(ServiceType.STATIC)
            .port(2300)
            .address("127.0.0.1")
            .id(String.format("bar-%s-%d-%s", bar.a, bar.b, bar.c))
            .name(bar.name)
            .build();
    }

    // the other required convert method.  This one converts RSR service instances (which are very generic) into
    // ServiceInstance<T> instances required by the Curator interfaces.
    public static ServiceInstance<BarCuratorService> convert(Service svc) throws Exception {
        BarCuratorService bar = new BarCuratorService(svc.getMetadata().get("name"),
                                        svc.getMetadata().get("a"),
                                        Integer.parseInt(svc.getMetadata().get("b")),
                                        Float.parseFloat(svc.getMetadata().get("c")));
        return convert(bar);
    }
}


package com.rackspacecloud.client.service_registry.curator;

import com.netflix.curator.utils.ThreadUtils;
import com.netflix.curator.x.discovery.ServiceCacheBuilder;
import com.netflix.curator.x.discovery.ServiceDiscovery;
import com.netflix.curator.x.discovery.ServiceInstance;
import com.netflix.curator.x.discovery.ServiceProviderBuilder;
import com.netflix.curator.x.discovery.strategies.RoundRobinStrategy;
import com.rackspacecloud.client.service_registry.Client;
import com.rackspacecloud.client.service_registry.MethodOptions;
import com.rackspacecloud.client.service_registry.objects.Service;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;

public class RSRServiceDiscoveryImpl<T> implements ServiceDiscovery<T> {

    private final Client client;
    private final Class<T> typeClass;
    private final String typeTag;
    private final Method convert;

    private final Map<String, ServiceTracker<T>> services = new HashMap<String, ServiceTracker<T>>(); // needs synchronized

    public RSRServiceDiscoveryImpl(Client client, Class<T> type) {
        // deep validation has already been done.
        try {
            Method m = type.getMethod("convert", Service.class);
            m.setAccessible(true);
            convert = m;
        } catch (NoSuchMethodException ex) {
            throw new MissingResourceException("Class does not implement static convert() method", type.getName(), "convert");
        } catch (Exception ex) {
            throw new MissingResourceException(ex.getMessage(), type.getName(), "convert");
        }

        this.client = client;
        this.typeClass = type;
        this.typeTag = Utils.sanitizeTag(type.getName());
    }
    public void start() throws Exception {
        // noop
    }

    public synchronized void registerService(ServiceInstance<T> service) throws Exception {
        ServiceTracker<T> tracker = services.get(service.getId());
        if (tracker == null) {
            tracker = new ServiceTracker<T>(client, service, typeTag);
            tracker.register();
            services.put(service.getId(), tracker);
        }
    }

    public void updateService(ServiceInstance<T> service) throws Exception {
        // todo: consider the implications of this implementation. unregistration forces some heartbeat events
        unregisterService(service);
        registerService(service);
    }

    public void unregisterService(ServiceInstance<T> service) throws Exception {
        client.getServicesClient().delete(service.getId());
        services.remove(service.getId()).stop();
    }

    public ServiceCacheBuilder<T> serviceCacheBuilder() {
        return new RSRServiceCacheBuilderImpl<T>(this)
                .threadFactory(ThreadUtils.newThreadFactory("RSRServiceCache"));
        // todo: whither 'name'?
    }

    /** return all distinct names registered by this discovery type. */
    public Collection<String> queryForNames() throws Exception {
        Set<String> names = new HashSet<String>();
        // todo: it would be better to do:
        // services = client.getServicesClient().list(options, typeTag);
        // but there are some validation problems (the tag is allowed to be written, but not queried on).
        Iterator<Service> services = client.getServicesClient().list(new MethodOptions(100, null));

        while (services.hasNext()) {
            Service service = services.next();
            // this conditional can be removed when the above operation works.
            if (!service.getTags().contains(typeTag)) {
                continue;
            }
            String name = service.getMetadata().get(ServiceTracker.NAME);
            if (!names.contains(name)) {
                names.add(name);
            }
        }
        return names;
    }

    /** return all instances registered to this particular name for this discovery type */
    public Collection<ServiceInstance<T>> queryForInstances(String name) throws Exception {
        List<ServiceInstance<T>> serviceInstances = new ArrayList<ServiceInstance<T>>();
        Iterator<Service> services = client.getServicesClient().list(new MethodOptions(100, null));
        while (services.hasNext()) {
            Service service = services.next();
            if (service.getTags().contains(typeTag) && service.getMetadata().get(ServiceTracker.NAME).equals(name)) {
                // does the job of the serializer in the curator code (theirs is just a json marshaller anyway).
                serviceInstances.add(convert(service));
            }
        }
        return serviceInstances;
    }

    public ServiceInstance<T> queryForInstance(String name, String id) throws Exception {
        return (ServiceInstance<T>) convert.invoke(typeClass, client.getServicesClient().get(id));
    }

    public ServiceProviderBuilder<T> serviceProviderBuilder() {
        return new RSRServiceProviderBuilderImpl<T>(this)
                // todo: what about these pieces?
                //.refreshPaddingMs(1000)
                //.serviceName("foo")
                .providerStrategy(new RoundRobinStrategy<T>())
                .threadFactory(ThreadUtils.newThreadFactory("RSRServiceProvider"));
    }

    public synchronized void close() throws IOException {
        for (ServiceTracker<T> tracker : services.values()) {
            tracker.stop();
        }
        services.clear();
    }

    //
    // helpers
    //

    public Client getClient() { return client; }
    public String getType() { return typeTag; }

    public ServiceInstance<T> convert(Service service) throws Exception {
        return (ServiceInstance<T>) convert.invoke(typeClass, service);
    }
}


package com.github.kmbulebu.nicknack.server.web;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.github.kmbulebu.nicknack.core.attributes.AttributeDefinition;
import com.github.kmbulebu.nicknack.core.states.StateDefinition;
import com.github.kmbulebu.nicknack.server.Application;
import com.github.kmbulebu.nicknack.server.model.AttributeDefinitionResource;
import com.github.kmbulebu.nicknack.server.model.StateDefinitionResource;
import com.github.kmbulebu.nicknack.server.services.StateDefinitionService;
import com.github.kmbulebu.nicknack.server.services.exceptions.AttributeDefinitionNotFoundException;
import com.github.kmbulebu.nicknack.server.services.exceptions.ProviderNotFoundException;
import com.github.kmbulebu.nicknack.server.services.exceptions.StateDefinitionNotFoundException;

@RestController
@RequestMapping(value="/api/stateDefinitions", produces={"application/hal+json"})
@ExposesResourceFor(StateDefinitionResource.class)
public class StateDefinitionsController {

	private static final Logger LOG = LogManager.getLogger(Application.APP_LOGGER_NAME);

	@Autowired
	private StateDefinitionService stateDefinitionService;

	@Autowired
	private RelProvider relProvider;

	@Autowired
	private EntityLinks entityLinks;

	// TODO Refactor HATEOAS link building. Time for ResourceAssembler or moving this stuff up the service. Although moving it up will create a circular dep.

	@RequestMapping(value="", method={RequestMethod.GET, RequestMethod.HEAD})
	public Resources<StateDefinitionResource> getStateDefinitions() throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry();
		}

		final Resources<StateDefinitionResource> resources = getStateDefinitions(stateDefinitionService.getStateDefinitions());

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

	@RequestMapping(value="", params="provider", method={RequestMethod.GET, RequestMethod.HEAD})
	public Resources<StateDefinitionResource> getStateDefinitions(@RequestParam UUID provider) throws ProviderNotFoundException, StateDefinitionNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry();
		}

		final Resources<StateDefinitionResource> resources = getStateDefinitions(stateDefinitionService.getStateDefinitionsByProvider(provider));

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

	private Resources<StateDefinitionResource> getStateDefinitions(Collection<StateDefinition> stateDefinitions) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(stateDefinitions);
		}

		final List<StateDefinitionResource> stateDefinitionResources = new ArrayList<StateDefinitionResource>(stateDefinitions.size());

		for (StateDefinition stateDefinition : stateDefinitions) {
			final StateDefinitionResource resource = new StateDefinitionResource(stateDefinition);
			resource.add(linkTo(methodOn(StateDefinitionsController.class).getStateDefinition(stateDefinition.getUUID())).withSelfRel());
			resource.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinitions(stateDefinition.getUUID())).withRel("attributeDefinitions"));
			stateDefinitionResources.add(resource);
		}

		final Resources<StateDefinitionResource> resources = new Resources<StateDefinitionResource>(stateDefinitionResources);
		resources.add(entityLinks.linkToCollectionResource(StateDefinitionResource.class));

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

	@RequestMapping(value="/{uuid}", method={RequestMethod.GET, RequestMethod.HEAD})
	public StateDefinitionResource getStateDefinition(@PathVariable UUID uuid) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(uuid);
		}

		final StateDefinition stateDefinition = stateDefinitionService.getStateDefinition(uuid);
		final StateDefinitionResource resource = new StateDefinitionResource(stateDefinition);
		resource.add(linkTo(methodOn(StateDefinitionsController.class).getStateDefinition(stateDefinition.getUUID())).withSelfRel());
		resource.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinitions(uuid)).withRel("attributeDefinitions"));

		if (LOG.isTraceEnabled()) {
			LOG.exit(resource);
		}
		return resource;
	}

	@RequestMapping(value="/{stateUuid}/attributeDefinitions", method={RequestMethod.GET, RequestMethod.HEAD})
	public Resources<AttributeDefinitionResource> getAttributeDefinitions(@PathVariable UUID stateUuid) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(stateUuid);
		}

		final List<AttributeDefinition> attributeDefinitions = stateDefinitionService.getAttributeDefinitions(stateUuid);
		final List<AttributeDefinitionResource> attributeDefinitionResources = new ArrayList<AttributeDefinitionResource>(attributeDefinitions.size());

		for (AttributeDefinition attributeDefinition : attributeDefinitions) {
			final AttributeDefinitionResource resource = new AttributeDefinitionResource(attributeDefinition);
			resource.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinition(stateUuid, attributeDefinition.getUUID())).withSelfRel());

			attributeDefinitionResources.add(resource);
		}

		final Resources<AttributeDefinitionResource> resources = new Resources<AttributeDefinitionResource>(attributeDefinitionResources);
		resources.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinitions(stateUuid)).withSelfRel());

		if (LOG.isTraceEnabled()) {
			LOG.exit(resources);
		}
		return resources;
	}

	@RequestMapping(value="/{stateUuid}/attributeDefinitions/{uuid}", method={RequestMethod.GET, RequestMethod.HEAD})
	public AttributeDefinitionResource getAttributeDefinition(@PathVariable UUID stateUuid, @PathVariable UUID uuid) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(stateUuid, uuid);
		}

		final AttributeDefinition attributeDefinition = stateDefinitionService.getAttributeDefinition(stateUuid, uuid);

		final AttributeDefinitionResource resource = new AttributeDefinitionResource(attributeDefinition);
		resource.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinition(stateUuid, attributeDefinition.getUUID())).withSelfRel());
		resource.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinitionValues(stateUuid, attributeDefinition.getUUID())).withRel("values"));

		if (LOG.isTraceEnabled()) {
			LOG.exit(resource);
		}
		return resource;
	}

	@RequestMapping(value="/{stateUuid}/attributeDefinitions/{uuid}/values", method={RequestMethod.GET, RequestMethod.HEAD})
	public ResponseEntity<Resource<Map<String, String>>> getAttributeDefinitionValues(@PathVariable UUID stateUuid, @PathVariable UUID uuid) throws StateDefinitionNotFoundException, ProviderNotFoundException, AttributeDefinitionNotFoundException {
		if (LOG.isTraceEnabled()) {
			LOG.entry(stateUuid, uuid);
		}

		final Map<String, String> attributeDefinitionValues = stateDefinitionService.getAttributeDefinitionValues(stateUuid, uuid);

		ResponseEntity<Resource<Map<String, String>>> response;
		if (attributeDefinitionValues == null) {
			response = new ResponseEntity<>(HttpStatus.NOT_FOUND);
		} else {
			final Resource<Map<String, String>> resource = new Resource<Map<String, String>>(attributeDefinitionValues);
			resource.add(linkTo(methodOn(StateDefinitionsController.class).getAttributeDefinitionValues(stateUuid, uuid)).withSelfRel());
			response = new ResponseEntity<>(resource, HttpStatus.OK);
		}

		if (LOG.isTraceEnabled()) {
			LOG.exit(response);
		}
		return response;
	}

}


package webster.spark;

import spark.utils.SparkUtils;

import java.util.*;

public class Spark {

    // taken from spark (https://github.com/perwendel/spark)
    public static Map<String, String> getParams(List<String> request, List<String> matched) {
        Map<String, String> params = new HashMap<>();

        for (int i = 0; (i < request.size()) && (i < matched.size()); i++) {
            String matchedPart = matched.get(i);
            if (SparkUtils.isParam(matchedPart)) {
                params.put(matchedPart.toLowerCase(), request.get(i));
            }
        }
        return Collections.unmodifiableMap(params);
    }

    // taken from spark (https://github.com/perwendel/spark)
    public static List<String> getSplat(List<String> request, List<String> matched) {
        int nbrOfRequestParts = request.size();
        int nbrOfMatchedParts = matched.size();

        boolean sameLength = (nbrOfRequestParts == nbrOfMatchedParts);

        List<String> splat = new ArrayList<>();

        for (int i = 0; (i < nbrOfRequestParts) && (i < nbrOfMatchedParts); i++) {
            String matchedPart = matched.get(i);

            if (SparkUtils.isSplat(matchedPart)) {

                StringBuilder splatParam = new StringBuilder(request.get(i));
                if (!sameLength && (i == (nbrOfMatchedParts - 1))) {
                    for (int j = i + 1; j < nbrOfRequestParts; j++) {
                        splatParam.append("/");
                        splatParam.append(request.get(j));
                    }
                }
                splat.add(splatParam.toString());
            }
        }
        return Collections.unmodifiableList(splat);
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.flow;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.item.FluidItem;

/**
 *

 *     Container POJO used to contain
 *     the {@code FluidItem} and {@code FlowStepRule}.
 *
 *

 *     Purely used to {@code compile} and {@code execute} a
 *     Fluid Rule against a {@code FluidItem} in realtime.
 *
 * @author jasonbruwer
 * @since v1.0
 * @see FluidItem
 * @see FlowStepRule
 */
public class FlowItemExecutePacket extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private FlowStepRule flowStepRule;
    private FluidItem fluidItem;

    /**
     * The JSON mapping for the {@code FlowItemExecutePacket} object.
     */
    public static class JSONMapping
    {
        public static final String FLOW_STEP_RULE = "flowStepRule";
        public static final String FLUID_ITEM = "fluidItem";
    }

    /**
     * Default constructor.
     */
    public FlowItemExecutePacket() {
        super();
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public FlowItemExecutePacket(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Fluid Item...
        if (!this.jsonObject.isNull(JSONMapping.FLOW_STEP_RULE)) {

            this.setFluidItem(new FluidItem(this.jsonObject.getJSONObject(
                    JSONMapping.FLUID_ITEM)));
        }

        //Flow Step Rule...
        if (!this.jsonObject.isNull(JSONMapping.FLOW_STEP_RULE)) {

            this.setFlowStepRule(new FlowStepRule(
                    this.jsonObject.getJSONObject(JSONMapping.FLOW_STEP_RULE)));
        }
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code FlowItemExecutePacket}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException
    {
        JSONObject returnVal = super.toJsonObject();

        //Fluid Item...
        if(this.getFluidItem() != null)
        {
            returnVal.put(JSONMapping.FLUID_ITEM,
                    this.getFluidItem().toJsonObject());
        }

        //Flow Step Rule...
        if(this.getFlowStepRule() != null)
        {
            returnVal.put(JSONMapping.FLOW_STEP_RULE,
                    this.getFlowStepRule().toJsonObject());
        }

        return returnVal;
    }

    /**
     * Gets the rule to execute.
     *
     * @return Rule to execute.
     *
     * @see FlowStepRule
     */
    public FlowStepRule getFlowStepRule() {
        return this.flowStepRule;
    }

    /**
     * Sets the rule to execute.
     *
     * @param flowStepRuleParam Rule to execute.
     *
     * @see FlowStepRule
     */
    public void setFlowStepRule(FlowStepRule flowStepRuleParam) {
        this.flowStepRule = flowStepRuleParam;
    }

    /**
     * Gets the Fluid item.
     *
     * @return Fluid item.
     *
     * @see FluidItem
     */
    public FluidItem getFluidItem() {
        return this.fluidItem;
    }

    /**
     * Sets the Fluid item.
     *
     * @param fluidItemParam Fluid item.
     *
     * @see FluidItem
     */
    public void setFluidItem(FluidItem fluidItemParam) {
        this.fluidItem = fluidItemParam;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.config;

import javax.xml.bind.annotation.XmlRootElement;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;

/**
 * Fluid configurations.
 *
 * @author jasonbruwer
 * @since v1.1
 *
 * @see ABaseFluidJSONObject
 */
@XmlRootElement
public class Configuration extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private String key;
    private String value;

    /**
     * The JSON mapping for the {@code Configuration} object.
     */
    public static class JSONMapping
    {
        public static final String KEY = "key";
        public static final String VALUE = "value";
    }

    /**
     * Default constructor.
     */
    public Configuration() {
        super();
    }

    /**
     * The unique Flow identifier.
     *
     * @param flowIdParam The Flow primary key.
     */
    public Configuration(Long flowIdParam) {
        super();

        this.setId(flowIdParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public Configuration(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Key...
        if (!this.jsonObject.isNull(JSONMapping.KEY)) {
            this.setKey(this.jsonObject.getString(JSONMapping.KEY));
        }

        //Value...
        if (!this.jsonObject.isNull(JSONMapping.VALUE)) {
            this.setValue(this.jsonObject.getString(JSONMapping.VALUE));
        }
    }

    /**
     * Gets the key of the Config.
     *
     * @return Config value.
     */
    public String getKey() {
        return this.key;
    }

    /**
     * Sets the Key of the Config.
     *
     * @param keyParam Config value.
     */
    public void setKey(String keyParam) {
        this.key = keyParam;
    }

    /**
     * Gets the value of the Config.
     *
     * @return Flow name.
     */
    public String getValue() {
        return this.value;
    }

    /**
     * Sets the Value of the Config.
     *
     * @param valueParam Config value.
     */
    public void setValue(String valueParam) {
        this.value = valueParam;
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code Flow}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException
    {
        JSONObject returnVal = super.toJsonObject();

        //Key...
        if(this.getKey() != null)
        {
            returnVal.put(JSONMapping.KEY,this.getKey());
        }

        //Value...
        if(this.getValue() != null)
        {
            returnVal.put(JSONMapping.VALUE, this.getValue());
        }

        return returnVal;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.form;

import java.util.List;

import org.json.JSONObject;

import com.fluidbpm.program.api.vo.form.Collaboration;
import com.fluidbpm.program.api.vo.form.CollaborationListing;
import com.fluidbpm.program.api.vo.form.Form;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.v1.ABaseClientWS;

/**
 * Java Web Service Client for Electronic Form related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see WS.Path.FormContainer
 * @see Form
 */
public class CollaborationClient extends ABaseClientWS {

    /**
     * Constructor that sets the Service Ticket from authentication.
     *
     * @param endpointBaseUrlParam URL to base endpoint.
     * @param serviceTicketParam The Server issued Service Ticket.
     */
    public CollaborationClient(
            String endpointBaseUrlParam,
            String serviceTicketParam) {
        super(endpointBaseUrlParam);

        this.setServiceTicket(serviceTicketParam);
    }

    /**
     * Create a new Form Container / Electronic Forms.
     *
     * @param collaborationsParam The Collaboration's to create.
     * @return Created Collaboration's.
     *
     * @see Collaboration
     */
    public List<Collaboration> createCollaboration(
        List<Collaboration> collaborationsParam
    ) {
        CollaborationListing collaborationListing = new CollaborationListing();
        collaborationListing.setListing(collaborationsParam);

        if(this.serviceTicket != null) {
            collaborationListing.setServiceTicket(this.serviceTicket);
        }

        return new CollaborationListing(this.putJson(
                collaborationListing, WS.Path.Collaboration.Version1.collaborationCreate())).getListing();
    }

    /**
     * Retrieve all Collaboration items TO where logged in user.
     *
     * @return Created Collaboration's.
     *
     * @see Collaboration
     */
    public List<Collaboration> getAllToByLoggedIn() {
        CollaborationListing collaborationListing = new CollaborationListing();
        if(this.serviceTicket != null) {
            collaborationListing.setServiceTicket(this.serviceTicket);
        }

        return new CollaborationListing(this.postJson(
                collaborationListing, WS.Path.Collaboration.Version1.getAllToByLoggedIn())).getListing();
    }

    /**
     * Retrieve all Collaboration items TO where {@code Form} is {@code formParam}.
     *
     * @param formParam The form to fetch collaborations to where sent 'TO'.
     *
     * @return Created Collaboration's based on {@code formParam}.
     *
     * @see Collaboration
     */
    public List<Collaboration> getAllToByForm(Form formParam) {

        if(formParam != null && this.serviceTicket != null) {
            formParam.setServiceTicket(this.serviceTicket);
        }

        return new CollaborationListing(this.postJson(
                formParam, WS.Path.Collaboration.Version1.getAllToByForm())).getListing();
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.form;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.field.Field;

/**
 * Represents an Fluid Table Record.
 *
 * @author jasonbruwer
 * @since v1.1
 *
 * @see Form
 * @see Field
 */
public class TableRecord extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private Form formContainer;
    private Form parentFormContainer;
    private Field parentFormField;

    /**
     * The JSON mapping for the {@code TableRecord} object.
     */
    public static class JSONMapping
    {
        public static final String FORM_CONTAINER= "formContainer";
        public static final String PARENT_FORM_CONTAINER= "parentFormContainer";
        public static final String PARENT_FORM_FIELD = "parentFormField";
    }

    /**
     * Sets the Id associated with a Field.
     *
     * @param tableRecordIdParam Field Id.
     */
    public TableRecord(Long tableRecordIdParam) {
        super();

        this.setId(tableRecordIdParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public TableRecord(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        //Form Container...
        if (!this.jsonObject.isNull(JSONMapping.FORM_CONTAINER)) {
            this.setFormContainer(
                    new Form(
                            this.jsonObject.getJSONObject(JSONMapping.FORM_CONTAINER)));
        }

        //Parent Form Container...
        if (!this.jsonObject.isNull(JSONMapping.PARENT_FORM_CONTAINER)) {
            this.setParentFormContainer(
                    new Form(
                            this.jsonObject.getJSONObject(JSONMapping.PARENT_FORM_CONTAINER)));
        }

        //Parent Field...
        if (!this.jsonObject.isNull(JSONMapping.PARENT_FORM_FIELD)) {
            this.setParentFormField(
                    new Field(this.jsonObject.getJSONObject(JSONMapping.PARENT_FORM_FIELD)));
        }
    }

    /**
	 * Default constructor.
	 */
    public TableRecord() {
        super();
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code Field}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException
    {
        JSONObject returnVal = super.toJsonObject();

        //Form Container...
        if(this.getFormContainer() != null)
        {
            returnVal.put(JSONMapping.FORM_CONTAINER,
                    this.getFormContainer().toJsonObject());
        }

        //Parent Form Container...
        if(this.getParentFormContainer() != null)
        {
            returnVal.put(JSONMapping.PARENT_FORM_CONTAINER,
                    this.getParentFormContainer().toJsonObject());
        }

        //Parent Form Field...
        if(this.getParentFormField() != null)
        {
            returnVal.put(JSONMapping.PARENT_FORM_FIELD,
                    this.getParentFormField().toJsonObject());
        }

        return returnVal;
    }

    /**
     * Gets the parent electronic Form Container.
     *
     * @return The parent Form.
     *
     * @see Form
     */
    public Form getParentFormContainer() {
        return this.parentFormContainer;
    }

    /**
     * Sets the parent electronic Form Container.
     *
     * @param parentFormContainerParam The parent Form.
     *
     * @see Form
     */
    public void setParentFormContainer(Form parentFormContainerParam) {
        this.parentFormContainer = parentFormContainerParam;
    }

    /**
     * Gets the electronic Form Container.
     *
     * @return The Form.
     *
     * @see Form
     */
    public Form getFormContainer() {
        return this.formContainer;
    }

    /**
     * Sets the electronic Form Container.
     *
     * @param formContainerParam The Form.
     *
     * @see Form
     */
    public void setFormContainer(Form formContainerParam) {
        this.formContainer = formContainerParam;
    }

    /**
     * Gets the parent electronic Form Field.
     *
     * @return The parent Form Field.
     *
     * @see Field
     */
    public Field getParentFormField() {
        return parentFormField;
    }

    /**
     * Sets the parent electronic Form Field.
     *
     * @param parentFormFieldParam The parent Form Field.
     *
     * @see Field
     */
    public void setParentFormField(Field parentFormFieldParam) {
        this.parentFormField = parentFormFieldParam;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.role;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.form.Form;

/**
 * <p>
 *     Represents the permission relationship between
 *     {@code Role} to {@code Field} and {@code Form}.
 * </p>
 *
 * @author jasonbruwer
 * @since v1.1
 *
 * @see Form
 * @see Field
 * @see Role
 */
public class RoleToFormFieldToFormDefinition extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private FormFieldToFormDefinition formFieldToFormDefinition;

    private Boolean canView;
    private Boolean canCreateAndModify;

    /**
     * The JSON mapping for the {@code RoleToFormFieldToFormDefinition} object.
     */
    public static class JSONMapping
    {
        public static final String FORM_FIELD_TO_FORM_DEFINITION =
                "formFieldToFormDefinition";
        public static final String CAN_VIEW = "canView";
        public static final String CAN_CREATE_AND_MODIFY = "canCreateAndModify";
    }

    /**
     * Default constructor.
     */
    public RoleToFormFieldToFormDefinition() {
        super();
    }

    /**
     * Sets the Id associated with a 'Form Field To Form Definition'.
     *
     * @param roleToFormFieldToFormDefinitionIdParam Field Id.
     */
    public RoleToFormFieldToFormDefinition(Long roleToFormFieldToFormDefinitionIdParam) {
        super();

        this.setId(roleToFormFieldToFormDefinitionIdParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public RoleToFormFieldToFormDefinition(JSONObject jsonObjectParam){
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Form Definition...
        if (!this.jsonObject.isNull(JSONMapping.FORM_FIELD_TO_FORM_DEFINITION)) {
            this.setFormFieldToFormDefinition(
                    new FormFieldToFormDefinition(this.jsonObject.getJSONObject(
                            JSONMapping.FORM_FIELD_TO_FORM_DEFINITION)));
        }

        //Can View...
        if (!this.jsonObject.isNull(JSONMapping.CAN_VIEW)) {
            this.setCanView(this.jsonObject.getBoolean(JSONMapping.CAN_VIEW));
        }

        //Can Create and Modify...
        if (!this.jsonObject.isNull(JSONMapping.CAN_CREATE_AND_MODIFY)) {
            this.setCanCreateAndModify(this.jsonObject.getBoolean(
                    JSONMapping.CAN_CREATE_AND_MODIFY));
        }
    }

    /**
     * Gets the {@code FormFieldToFormDefinition} relationship.
     *
     * @return {@code FormFieldToFormDefinition}.
     */
    public FormFieldToFormDefinition getFormFieldToFormDefinition() {
        return this.formFieldToFormDefinition;
    }

    /**
     * Sets the {@code FormFieldToFormDefinition} relationship.
     *
     * @param formFieldToFormDefinitionParam {@code FormFieldToFormDefinition}.
     */
    public void setFormFieldToFormDefinition(
            FormFieldToFormDefinition formFieldToFormDefinitionParam) {

        this.formFieldToFormDefinition = formFieldToFormDefinitionParam;
    }

    /**
     * Gets whether a {@code Field} may be viewed.
     *
     * @return Whether the {@code Form}, {@code Field} and role
     * relationship allows for viewing.
     *
     * @see Field
     * @see Form
     * @see Role
     */
    public Boolean isCanView() {
        return this.canView;
    }

    /**
     * Gets whether a {@code Field} may be viewed.
     *
     * @param canViewParam Whether the {@code Form}, {@code Field} and role
     * relationship allows for viewing.
     *
     * @see Field
     * @see Form
     * @see Role
     */
    public void setCanView(Boolean canViewParam) {
        this.canView = canViewParam;
    }

    /**
     * Gets whether a {@code Field} may created and modified.
     *
     * @return Whether the {@code Form}, {@code Field} and role
     * relationship allows for creation and modification.
     *
     * @see Field
     * @see Form
     * @see Role
     */
    public Boolean isCanCreateAndModify() {
        return this.canCreateAndModify;
    }

    /**
     * Gets whether a {@code Field} may created and modified.
     *
     * @param canCreateAndModifyParam Whether the {@code Form}, {@code Field} and role
     * relationship allows for creation and modification.
     *
     * @see Field
     * @see Form
     * @see Role
     */
    public void setCanCreateAndModify(Boolean canCreateAndModifyParam) {
        this.canCreateAndModify = canCreateAndModifyParam;
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code RoleToFormFieldToFormDefinition}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException {

        JSONObject returnVal = super.toJsonObject();

        //Can Create...
        if(this.isCanCreateAndModify() != null)
        {
            returnVal.put(JSONMapping.CAN_CREATE_AND_MODIFY,
                    this.isCanCreateAndModify().booleanValue());
        }

        //Can View...
        if(this.isCanView() != null)
        {
            returnVal.put(JSONMapping.CAN_VIEW,
                    this.isCanView().booleanValue());
        }

        //Form Definition...
        if(this.getFormFieldToFormDefinition() != null)
        {
            returnVal.put(JSONMapping.FORM_FIELD_TO_FORM_DEFINITION,
                    this.getFormFieldToFormDefinition().toJsonObject());
        }

        return returnVal;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.template;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.flow.Flow;
import com.fluidbpm.program.api.vo.form.Form;
import com.fluidbpm.program.api.vo.thirdpartylib.ThirdPartyLibrary;
import com.fluidbpm.program.api.vo.userquery.UserQuery;

/**
 * Fluid Template used to import and export configurations;
 *
 * {@code Flow}'s
 * {@code Form}'s
 * {@code Field}'s
 * {@code UserQuery}'s
 *
 * @see Form
 * @see Field
 *
 * @see com.fluidbpm.program.api.vo.userquery.UserQuery
 * @see com.fluidbpm.program.api.vo.flow.Flow
 * @see com.fluidbpm.program.api.vo.thirdpartylib.ThirdPartyLibrary
 *
 * @see com.fluidbpm.program.api.vo.ABaseFluidJSONObject
 *
 * @author jasonbruwer on 2017/01/14.
 * @since 1.4
 */
public class FluidTemplate extends ABaseFluidJSONObject{

    public static final long serialVersionUID = 1L;

    private String templateName;
    private String templateDescription;
    private String templateComment;

    private List<Form> formsAndFields;
    private List<UserQuery> userQueries;
    private List<Flow> flows;

    private List<ThirdPartyLibrary> thirdPartyLibraries;

    private List<Field> userFields;
    private List<Field> routeFields;
    private List<Field> globalFields;

    /**
     * The JSON mapping for the {@code FluidTemplate} object.
     */
    public static final class JSONMapping {
        public static final String TEMPLATE_NAME = "templateName";
        public static final String TEMPLATE_DESCRIPTION = "templateDescription";
        public static final String TEMPLATE_COMMENT = "templateComment";

        public static final String FORMS_AND_FIELDS = "formsAndFields";
        public static final String USER_QUERIES = "userQueries";
        public static final String FLOWS = "flows";

        public static final String THIRD_PARTY_LIBRARIES = "thirdPartyLibraries";

        public static final String USER_FIELDS = "userFields";
        public static final String ROUTE_FIELDS = "routeFields";
        public static final String GLOBAL_FIELDS = "globalFields";
    }

    /**
     * Default constructor.
     */
    public FluidTemplate() {
        super();
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public FluidTemplate(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        //Template Name...
        if (!this.jsonObject.isNull(JSONMapping.TEMPLATE_NAME)) {
            this.setTemplateName(this.jsonObject.getString(
                    JSONMapping.TEMPLATE_NAME));
        }

        //Template Description...
        if (!this.jsonObject.isNull(JSONMapping.TEMPLATE_DESCRIPTION)) {
            this.setTemplateDescription(this.jsonObject.getString(
                    JSONMapping.TEMPLATE_DESCRIPTION));
        }

        //Template Comment...
        if (!this.jsonObject.isNull(JSONMapping.TEMPLATE_COMMENT)) {
            this.setTemplateComment(this.jsonObject.getString(
                    JSONMapping.TEMPLATE_COMMENT));
        }

        //Forms and Fields...
        if (!this.jsonObject.isNull(JSONMapping.FORMS_AND_FIELDS)) {

            JSONArray formsAndFields = this.jsonObject.getJSONArray(
                    JSONMapping.FORMS_AND_FIELDS);

            List<Form> listOfForms = new ArrayList();
            for(int index = 0;index < formsAndFields.length();index++)
            {
                listOfForms.add(new Form(formsAndFields.getJSONObject(index)));
            }

            this.setFormsAndFields(listOfForms);
        }

        //User Queries...
        if (!this.jsonObject.isNull(JSONMapping.USER_QUERIES)) {

            JSONArray userQueries = this.jsonObject.getJSONArray(
                    JSONMapping.USER_QUERIES);

            List<UserQuery> listOfUserQueries = new ArrayList();
            for(int index = 0;index < userQueries.length();index++)
            {
                listOfUserQueries.add(new UserQuery(userQueries.getJSONObject(index)));
            }

            this.setUserQueries(listOfUserQueries);
        }

        //Flows...
        if (!this.jsonObject.isNull(JSONMapping.FLOWS)) {

            JSONArray flows = this.jsonObject.getJSONArray(
                    JSONMapping.FLOWS);

            List<Flow> listOfFlows = new ArrayList();
            for(int index = 0;index < flows.length();index++)
            {
                listOfFlows.add(new Flow(flows.getJSONObject(index)));
            }

            this.setFlows(listOfFlows);
        }

        //Third Party Libraries...
        if (!this.jsonObject.isNull(JSONMapping.THIRD_PARTY_LIBRARIES)) {

            JSONArray thirdPartyLibs = this.jsonObject.getJSONArray(
                    JSONMapping.THIRD_PARTY_LIBRARIES);

            List<ThirdPartyLibrary> listOfThirdPartyLibs = new ArrayList();
            for(int index = 0;index < thirdPartyLibs.length();index++)
            {
                listOfThirdPartyLibs.add(
                        new ThirdPartyLibrary(thirdPartyLibs.getJSONObject(index)));
            }

            this.setThirdPartyLibraries(listOfThirdPartyLibs);
        }

        //User Fields...
        if (!this.jsonObject.isNull(JSONMapping.USER_FIELDS)) {

            JSONArray userFields = this.jsonObject.getJSONArray(
                    JSONMapping.USER_FIELDS);

            List<Field> listOfFields = new ArrayList();
            for(int index = 0;index < userFields.length();index++)
            {
                listOfFields.add(new Field(userFields.getJSONObject(index)));
            }
            this.setUserFields(listOfFields);
        }

        //Route Fields...
        if (!this.jsonObject.isNull(JSONMapping.ROUTE_FIELDS)) {

            JSONArray routeFields = this.jsonObject.getJSONArray(
                    JSONMapping.ROUTE_FIELDS);

            List<Field> listOfFields = new ArrayList();
            for(int index = 0;index < routeFields.length();index++)
            {
                listOfFields.add(new Field(routeFields.getJSONObject(index)));
            }
            this.setRouteFields(listOfFields);
        }

        //Global Fields...
        if (!this.jsonObject.isNull(JSONMapping.GLOBAL_FIELDS)) {

            JSONArray globalFields = this.jsonObject.getJSONArray(
                    JSONMapping.GLOBAL_FIELDS);

            List<Field> listOfFields = new ArrayList();
            for(int index = 0;index < globalFields.length();index++)
            {
                listOfFields.add(new Field(globalFields.getJSONObject(index)));
            }
            this.setGlobalFields(listOfFields);
        }
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code FluidTemplate}
     * @throws JSONException If there is a problem with the JSON Body.
     */
    @Override
    public JSONObject toJsonObject() throws JSONException {

        JSONObject returnVal = new JSONObject();

        //Template Name...
        if(this.getTemplateName() != null)
        {
            returnVal.put(JSONMapping.TEMPLATE_NAME,
                    this.getTemplateName());
        }

        //Template Description...
        if(this.getTemplateDescription() != null)
        {
            returnVal.put(JSONMapping.TEMPLATE_DESCRIPTION,
                    this.getTemplateDescription());
        }

        //Template Comment...
        if(this.getTemplateComment() != null)
        {
            returnVal.put(JSONMapping.TEMPLATE_COMMENT,
                    this.getTemplateComment());
        }

        //Forms and Fields...
        if(this.getFormsAndFields() != null && !this.getFormsAndFields().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(Form form : this.getFormsAndFields())
            {
                jsonArray.put(form.toJsonObject());
            }

            returnVal.put(JSONMapping.FORMS_AND_FIELDS, jsonArray);
        }

        //User Queries...
        if(this.getUserQueries() != null && !this.getUserQueries().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(UserQuery userQuery : this.getUserQueries())
            {
                jsonArray.put(userQuery.toJsonObject());
            }

            returnVal.put(JSONMapping.USER_QUERIES, jsonArray);
        }

        //Flows...
        if(this.getFlows() != null && !this.getFlows().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(Flow flow : this.getFlows())
            {
                jsonArray.put(flow.toJsonObject());
            }

            returnVal.put(JSONMapping.FLOWS, jsonArray);
        }

        //Third Party Libraries...
        if(this.getThirdPartyLibraries() != null &&
                !this.getThirdPartyLibraries().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(ThirdPartyLibrary thirdPartyLibrary : this.getThirdPartyLibraries())
            {
                jsonArray.put(thirdPartyLibrary.toJsonObject());
            }

            returnVal.put(JSONMapping.THIRD_PARTY_LIBRARIES, jsonArray);
        }

        //User Fields...
        if(this.getUserFields() != null && !this.getUserFields().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(Field field : this.getUserFields())
            {
                jsonArray.put(field.toJsonObject());
            }

            returnVal.put(JSONMapping.USER_FIELDS, jsonArray);
        }

        //Route Fields...
        if(this.getRouteFields() != null && !this.getRouteFields().isEmpty())
        {
            JSONArray fieldsArr = new JSONArray();
            for(Field toAdd :this.getRouteFields())
            {
                fieldsArr.put(toAdd.toJsonObject());
            }

            returnVal.put(JSONMapping.ROUTE_FIELDS, fieldsArr);
        }

        //Global Fields...
        if(this.getGlobalFields() != null && !this.getGlobalFields().isEmpty())
        {
            JSONArray fieldsArr = new JSONArray();
            for(Field toAdd :this.getGlobalFields())
            {
                fieldsArr.put(toAdd.toJsonObject());
            }

            returnVal.put(JSONMapping.GLOBAL_FIELDS, fieldsArr);
        }

        return returnVal;
    }

    /**
     * Gets Name of the template.
     *
     * @return Template name.
     */
    public String getTemplateName() {
        return this.templateName;
    }

    /**
     * Sets Name of the template.
     *
     * @param templateNameParam Fluid Template name.
     */
    public void setTemplateName(String templateNameParam) {
        this.templateName = templateNameParam;
    }

    /**
     * Gets Description of the template.
     *
     * @return Template description.
     */
    public String getTemplateDescription() {
        return this.templateDescription;
    }

    /**
     * Sets Description of the template.
     *
     * @param templateDescriptionParam Fluid Template description.
     */
    public void setTemplateDescription(String templateDescriptionParam) {
        this.templateDescription = templateDescriptionParam;
    }

    /**
     * Gets Comment of the template.
     *
     * @return Template description.
     */
    public String getTemplateComment() {
        return this.templateComment;
    }

    /**
     * Sets Comment of the template.
     *
     * @param templateCommentParam Fluid Comment description.
     */
    public void setTemplateComment(String templateCommentParam) {
        this.templateComment = templateCommentParam;
    }

    /**
     * Template Forms and Fields.
     *
     * @return Forms and Fields.
     */
    public List<Form> getFormsAndFields() {
        return this.formsAndFields;
    }

    /**
     * Sets Form's and Fields for the template.
     *
     * @param formsAndFieldsParam Fluid Forms and Fields.
     */
    public void setFormsAndFields(List<Form> formsAndFieldsParam) {
        this.formsAndFields = formsAndFieldsParam;
    }

    /**
     * Sets the template {@code UserQuery}.
     *
     * @return User Queries.
     */
    public List<UserQuery> getUserQueries() {
        return this.userQueries;
    }

    /**
     * Sets the template {@code UserQuery}.
     *
     * @param userQueriesParam User Queries.
     */
    public void setUserQueries(List<UserQuery> userQueriesParam) {
        this.userQueries = userQueriesParam;
    }

    /**
     * Gets Flows.
     *
     * @return Template name.
     */
    public List<Flow> getFlows() {
        return this.flows;
    }

    /**
     * Sets Flows for the template.
     *
     * @param flowsParam Flow's for the template.
     */
    public void setFlows(List<Flow> flowsParam) {
        this.flows = flowsParam;
    }

    /**
     * Gets the Third Party Libraries.
     *
     * @return 3rd Party Libraries.
     */
    public List<ThirdPartyLibrary> getThirdPartyLibraries() {
        return this.thirdPartyLibraries;
    }

    /**
     * Sets the Third Party libraries.
     *
     * @param thirdPartyLibrariesParam 3rd Party Libraries.
     */
    public void setThirdPartyLibraries(List<ThirdPartyLibrary> thirdPartyLibrariesParam) {
        this.thirdPartyLibraries = thirdPartyLibrariesParam;
    }

    /**
     * Gets the User {@code Field}s.
     *
     * @return User fields.
     */
    public List<Field> getUserFields() {
        return this.userFields;
    }

    /**
     * Sets the User {@code Field}s.
     *
     * @param userFieldsParam User fields.
     */
    public void setUserFields(List<Field> userFieldsParam) {
        this.userFields = userFieldsParam;
    }

    /**
     * Gets the Route {@code Field}s.
     *
     * @return Route fields.
     */
    public List<Field> getRouteFields() {
        return this.routeFields;
    }

    /**
     * Sets the Route {@code Field}s.
     *
     * @param routeFieldsParam Route fields.
     */
    public void setRouteFields(List<Field> routeFieldsParam) {
        this.routeFields = routeFieldsParam;
    }

    /**
     * Gets the Global {@code Field}s.
     *
     * @return Global fields.
     */
    public List<Field> getGlobalFields() {
        return this.globalFields;
    }

    /**
     * Sets the Global {@code Field}s.
     *
     * @param globalFieldsParam Global fields.
     */
    public void setGlobalFields(List<Field> globalFieldsParam) {
        this.globalFields = globalFieldsParam;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.flow;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;

/**
 * Fluid consolidation of a workflow.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see FlowStep
 * @see FlowStepRule
 * @see ABaseFluidJSONObject
 */
public class Flow extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private String name;
    private String description;
    private Date dateCreated;
    private Date dateLastUpdated;
    private List<FlowStep> flowSteps;

    /**
     * The JSON mapping for the {@code Flow} object.
     */
    public static class JSONMapping
    {
        public static final String NAME = "name";
        public static final String DESCRIPTION = "description";
        public static final String DATE_CREATED = "dateCreated";
        public static final String DATE_LAST_UPDATED = "dateLastUpdated";
        public static final String FLOW_STEPS = "flowSteps";
    }

    /**
     * Default constructor.
     */
    public Flow() {
        super();
    }

    /**
     * Constructor with the unique Flow identifier.
     *
     * @param flowIdParam The Flow primary key.
     */
    public Flow(Long flowIdParam) {
        super();

        this.setId(flowIdParam);
    }

    /**
     * Constructor with the Flow name.
     *
     * @param flowNameParam The Flow name.
     */
    public Flow(String flowNameParam) {
        super();

        this.setName(flowNameParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public Flow(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Name...
        if (!this.jsonObject.isNull(JSONMapping.NAME)) {
            this.setName(this.jsonObject.getString(JSONMapping.NAME));
        }

        //Description...
        if (!this.jsonObject.isNull(JSONMapping.DESCRIPTION)) {
            this.setDescription(this.jsonObject.getString(JSONMapping.DESCRIPTION));
        }

        //Date Created...
        this.setDateCreated(this.getDateFieldValueFromFieldWithName(JSONMapping.DATE_CREATED));

        //Date Last Updated...
        this.setDateLastUpdated(this.getDateFieldValueFromFieldWithName(JSONMapping.DATE_LAST_UPDATED));

        //Flow Steps...
        if (!this.jsonObject.isNull(JSONMapping.FLOW_STEPS)) {

            JSONArray entryRules = this.jsonObject.getJSONArray(JSONMapping.FLOW_STEPS);

            List<FlowStep> listOfFlowSteps = new ArrayList();
            for(int index = 0;index < entryRules.length();index++)
            {
                listOfFlowSteps.add(new FlowStep(entryRules.getJSONObject(index)));
            }

            this.setFlowSteps(listOfFlowSteps);
        }
    }

    /**
     * Gets the Name of the Flow.
     *
     * @return Flow name.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Sets the Name of the Flow.
     *
     * @param nameParam Flow name.
     */
    public void setName(String nameParam) {
        this.name = nameParam;
    }

    /**
     * Gets the Description of the Flow.
     *
     * @return Flow description.
     */
    public String getDescription() {
        return this.description;
    }

    /**
     * Sets the Description of the Flow.
     *
     * @param descriptionParam Flow description.
     */
    public void setDescription(String descriptionParam) {
        this.description = descriptionParam;
    }

    /**
     * Gets the date the Flow was created.
     *
     * @return The Flow creation date.
     */
    public Date getDateCreated() {
        return this.dateCreated;
    }

    /**
     * Sets the date the Flow was created.
     *
     * @param dateCreatedParam The Flow creation date.
     */
    public void setDateCreated(Date dateCreatedParam) {
        this.dateCreated = dateCreatedParam;
    }

    /**
     * Gets the date the Flow was last updated.
     *
     * @return Last Updated Timestamp.
     */
    public Date getDateLastUpdated() {
        return this.dateLastUpdated;
    }

    /**
     * Sets the date the Flow was last updated.
     *
     * @param dateLastUpdatedParam Last Updated Timestamp.
     */
    public void setDateLastUpdated(Date dateLastUpdatedParam) {
        this.dateLastUpdated = dateLastUpdatedParam;
    }

    /**
     * Gets the Flow steps.
     *
     * @return Flow Steps.
     */
    public List<FlowStep> getFlowSteps() {
        return this.flowSteps;
    }

    /**
     * Sets the Flow steps.
     *
     * @param flowStepsParam The Flow Steps.
     */
    public void setFlowSteps(List<FlowStep> flowStepsParam) {
        this.flowSteps = flowStepsParam;
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code Flow}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException
    {
        JSONObject returnVal = super.toJsonObject();

        //Name...
        if(this.getName() != null)
        {
            returnVal.put(JSONMapping.NAME,this.getName());
        }

        //Description...
        if(this.getDescription() != null)
        {
            returnVal.put(JSONMapping.DESCRIPTION, this.getDescription());
        }

        //Flow Steps...
        if(this.getFlowSteps() != null && !this.getFlowSteps().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(FlowStep rule : this.getFlowSteps())
            {
                jsonArray.put(rule.toJsonObject());
            }

            returnVal.put(JSONMapping.FLOW_STEPS, jsonArray);
        }

        //Date Created...
        if(this.getDateCreated() != null)
        {
            returnVal.put(JSONMapping.DATE_CREATED, this.getDateAsLongFromJson(
                    this.getDateCreated()));
        }

        //Date Last Updated...
        if(this.getDateLastUpdated() != null)
        {
            returnVal.put(JSONMapping.DATE_LAST_UPDATED, this.getDateAsLongFromJson(
                    this.getDateLastUpdated()));
        }

        return returnVal;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1;

import org.json.JSONObject;

/**
 * Base class for all Field REST related calls.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.FormField
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.UserField
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.RouteField
 */
public abstract class ABaseFieldClient extends ABaseClientWS {

	/**
	 * Creates a new client and sets the Base Endpoint URL.
	 *
	 * @param endpointBaseUrlParam URL to base endpoint.
	 */
	public ABaseFieldClient(String endpointBaseUrlParam) {
		super(endpointBaseUrlParam);
	}

	/**
	 * Meta-Data types for Field.
	 */
	public static final class FieldMetaData {
		/**
		 * Meta-Data types for Text fields specifically.
		 */
		public static final class Text {
			public static final String PLAIN = "Plain";
			public static final String MASKED = "Masked";
			public static final String BARCODE = "Barcode";
			public static final String LATITUDE_AND_LONGITUDE = "Latitude and Longitude";
		}

		/**
		 * Meta-Data types for True False fields specifically.
		 */
		public static final class TrueFalse {
			public static final String TRUE_FALSE = "True False";
		}

		/**
		 * Meta-Data types for Paragraph Text fields specifically.
		 */
		public static final class ParagraphText {
			public static final String PLAIN = "Plain";
			public static final String HTML = "HTML";
		}

		/**
		 * Meta-Data types for Multi Choice fields specifically.
		 */
		public static final class MultiChoice {
			public static final String PLAIN = "Plain";
			public static final String PLAIN_SEARCH = "Plain with Search";

			public static final String SELECT_MANY = "Select Many";
			public static final String SELECT_MANY_SEARCH = "Select Many with Search";
		}

		/**
		 * Meta-Data types for Date Time fields specifically.
		 */
		public static final class DateTime {
			public static final String DATE = "Date";
			public static final String DATE_AND_TIME = "Date and Time";
		}

		/**
		 * Meta-Data types for Decimal fields specifically.
		 */
		public static final class Decimal {
			public static final String PLAIN = "Plain";
			public static final String RATING = "Rating";
			public static final String SPINNER = "Spinner";
			public static final String SLIDER = "Slider";

			//Keywords...
			public static final String UNDERSCORE = "_";
			public static final String SQ_OPEN = "[";
			public static final String SQ_CLOSE = "]";

			public static final String MIN = "Min";
			public static final String MAX = "Max";
			public static final String STEP_FACTOR = "StepFactor";
			public static final String PREFIX = "Prefix";
		}

		/**
		 * Meta-Data types for Table Field fields specifically.
		 */
		public static final class TableField {

			public static final String SUM_DECIMALS = "SumDecimals";

			public static final String UNDERSCORE = "_";
			public static final String SQ_OPEN = "[";
			public static final String SQ_CLOSE = "]";
		}
	}

	/**
	 * Construct the correct Meta-Data from parameters.
	 *
	 * @param metaDataPrefixParam Prefix for return val to indicate type. Example;
	 *            Spinner.
	 * @param minParam The min allowed value.
	 * @param maxParam The max allowed value.
	 * @param stepFactorParam The allowed step increments.
	 * @param prefixParam The prefix for the decimal, like $ for currency.
	 * @return Correctly formatted Decimal type.
	 */
	protected String getMetaDataForDecimalAs(
			String metaDataPrefixParam,
			double minParam,
			double maxParam,
			double stepFactorParam,
			String prefixParam
	) {
		StringBuffer returnBuffer = new StringBuffer();

		if (metaDataPrefixParam != null && !metaDataPrefixParam.isEmpty()) {
			returnBuffer.append(metaDataPrefixParam);
		}

		//Min...
		returnBuffer.append(FieldMetaData.Decimal.UNDERSCORE);
		returnBuffer.append(FieldMetaData.Decimal.MIN);
		returnBuffer.append(FieldMetaData.Decimal.SQ_OPEN);
		returnBuffer.append(minParam);
		returnBuffer.append(FieldMetaData.Decimal.SQ_CLOSE);
		returnBuffer.append(FieldMetaData.Decimal.UNDERSCORE);

		//Max...
		returnBuffer.append(FieldMetaData.Decimal.MAX);
		returnBuffer.append(FieldMetaData.Decimal.SQ_OPEN);
		returnBuffer.append(maxParam);
		returnBuffer.append(FieldMetaData.Decimal.SQ_CLOSE);
		returnBuffer.append(FieldMetaData.Decimal.UNDERSCORE);

		//Step Factor...
		returnBuffer.append(FieldMetaData.Decimal.STEP_FACTOR);
		returnBuffer.append(FieldMetaData.Decimal.SQ_OPEN);
		returnBuffer.append(stepFactorParam);
		returnBuffer.append(FieldMetaData.Decimal.SQ_CLOSE);
		returnBuffer.append(FieldMetaData.Decimal.UNDERSCORE);

		//Prefix
		String prefix = (prefixParam == null) ? "" : prefixParam;

		returnBuffer.append(FieldMetaData.Decimal.PREFIX);
		returnBuffer.append(FieldMetaData.Decimal.SQ_OPEN);
		returnBuffer.append(prefix);
		returnBuffer.append(FieldMetaData.Decimal.SQ_CLOSE);

		return returnBuffer.toString();
	}
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.user;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.flow.JobView;
import com.fluidbpm.program.api.vo.role.Role;
import com.fluidbpm.program.api.vo.user.User;
import com.fluidbpm.program.api.vo.user.UserFieldListing;
import com.fluidbpm.program.api.vo.user.UserListing;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.FluidClientException;
import com.fluidbpm.ws.client.v1.ABaseClientWS;

/**
 * Java Web Service Client for User related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.User
 * @see User
 */
public class UserClient extends ABaseClientWS {

    private static final String JSON_TAG_DATA = "data";

    /**
     * Constructor that sets the Service Ticket from authentication.
     *
     * @param endpointBaseUrlParam URL to base endpoint.
     * @param serviceTicketParam The Server issued Service Ticket.
     */
    public UserClient(String endpointBaseUrlParam, String serviceTicketParam) {
        super(endpointBaseUrlParam);

        this.setServiceTicket(serviceTicketParam);
    }

    /**
     * Creates a new {@code User} with the Email, Fields and
     * Roles inside the {@code userParam}.
     *
     * @param userParam The {@code User} to create.
     * @return The Created User.
     *
     * @see com.fluidbpm.program.api.vo.user.User
     * @see Field
     * @see com.fluidbpm.program.api.vo.role.Role
     */
    public User createUser(User userParam)
    {
        if(userParam != null && this.serviceTicket != null)
        {
            userParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.putJson(
                userParam, WS.Path.User.Version1.userCreate()));
    }

    /**
     * Updates an existing {@code User} with the Email, Fields and
     * Roles inside the {@code userParam}.
     *
     * @param userParam The User to update.
     * @return The Updated User.
     *
     * @see com.fluidbpm.program.api.vo.user.User
     * @see Field
     * @see com.fluidbpm.program.api.vo.role.Role
     */
    public User updateUser(User userParam)
    {
        if(userParam != null && this.serviceTicket != null)
        {
            userParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.postJson(
                userParam, WS.Path.User.Version1.userUpdate()));
    }

    /**
     * Activate an existing {@code User} that is currently
     * Deactivated.
     *
     * @param userParam The User to activate.
     * @return The Activated User.
     *
     * @see com.fluidbpm.program.api.vo.user.User
     * @see Field
     * @see com.fluidbpm.program.api.vo.role.Role
     */
    public User activateUser(User userParam)
    {
        if(userParam != null && this.serviceTicket != null)
        {
            userParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.postJson(
                userParam,
                WS.Path.User.Version1.userActivate()));
    }

    /**
     * Deactivate an existing {@code User} that is currently
     * Active.
     *
     * @param userParam The User to De-Activate.
     * @return The DeActivated User.
     *
     * @see com.fluidbpm.program.api.vo.user.User
     * @see Field
     * @see com.fluidbpm.program.api.vo.role.Role
     */
    public User deActivateUser(User userParam)
    {
        if(userParam != null && this.serviceTicket != null)
        {
            userParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.postJson(
                userParam,
                WS.Path.User.Version1.userDeActivate()));
    }

    /**
     * Increment the invalid login count for {@code userParam}.
     *
     * @param userParam The User to increment invalid count for.
     * @return The User where invalid count performed.
     *
     * @see com.fluidbpm.program.api.vo.user.User
     */
    public User incrementInvalidLoginForUser(User userParam)
    {
        if(userParam != null && this.serviceTicket != null)
        {
            userParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.postJson(
                userParam,
                WS.Path.User.Version1.incrementInvalidLogin()));
    }

    /**
     * Change the password for the currently logged in user.
     *
     * @param existingPasswordParam The current password.
     * @param newPasswordParam The new password.
     * @param confirmNewPasswordParam The new password again.
     *
     * @return {@code User} which password was changed.
     */
    public User changePasswordForLoggedInUser(
            String existingPasswordParam,
            String newPasswordParam,
            String confirmNewPasswordParam) {
        User toChangePasswordFor = new User();
        if(this.serviceTicket != null) {
            toChangePasswordFor.setServiceTicket(this.serviceTicket);
        }

        String existingPassword =
                existingPasswordParam == null ? UtilGlobal.EMPTY: existingPasswordParam;
        String newPassword =
                newPasswordParam == null ? UtilGlobal.EMPTY: newPasswordParam;
        String confirmNewPassword =
                confirmNewPasswordParam == null ? UtilGlobal.EMPTY: confirmNewPasswordParam;

        JSONObject passwordClear = new JSONObject();
        passwordClear.put("existing",existingPassword);
        passwordClear.put("new",newPassword);
        passwordClear.put("confirm_new",confirmNewPassword);

        toChangePasswordFor.setPasswordClear(passwordClear.toString());

        return new User(this.postJson(
                toChangePasswordFor,
                WS.Path.User.Version1.changePassword()));
    }

    /**
     * Deletes the {@code User} provided.
     * Id must be set on the {@code User}.
     *
     * @param userToDeleteParam The User to Delete.
     * @return The deleted User.
     */
    public User deleteUser(User userToDeleteParam)
    {
        if(userToDeleteParam != null && this.serviceTicket != null)
        {
            userToDeleteParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.postJson(userToDeleteParam,
                WS.Path.User.Version1.userDelete()));
    }

    /**
     * Deletes the {@code User} provided.
     * Id must be set on the {@code User}.
     *
     * @param userToDeleteParam The User to Delete.
     * @param forcefullyDeleteParam Delete the User forcefully.
     * @return The deleted User.
     */
    public User deleteUser(
            User userToDeleteParam,
            boolean forcefullyDeleteParam)
    {
        if(userToDeleteParam != null && this.serviceTicket != null)
        {
            userToDeleteParam.setServiceTicket(this.serviceTicket);
        }

        return new User(this.postJson(userToDeleteParam,
                WS.Path.User.Version1.userDelete(forcefullyDeleteParam)));
    }

    /**
     * Retrieves user information for the logged in {@code User}.
     *
     * @return User information.
     *
     * @see User
     */
    public User getLoggedInUserInformation()
    {
        User userToGetInfoFor = new User();

        if(this.serviceTicket != null)
        {
            userToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new User(this.postJson(
                    userToGetInfoFor,
                    WS.Path.User.Version1.userInformation()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves user information for the provided {@code usernameParam}.
     *
     * @param usernameParam The username of the user to retrieve info for.
     * @return User information.
     *
     * @see User
     */
    public User getUserWhereUsername(String usernameParam)
    {
        User userToGetInfoFor = new User();
        userToGetInfoFor.setUsername(usernameParam);

        if(this.serviceTicket != null)
        {
            userToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new User(this.postJson(
                    userToGetInfoFor, WS.Path.User.Version1.getByUsername()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves user information for the provided {@code emailAddressParam}.
     *
     * The email address must be confirmed.
     *
     * @param emailAddressParam The confirmed Email of the user to retrieve info for.
     * @return User information.
     *
     * @see User
     */
    public User getUserWhereEmail(String emailAddressParam)
    {
        User userToGetInfoFor = new User();

        if(emailAddressParam != null)
        {
            List<String> emailAdd = new ArrayList();
            emailAdd.add(emailAddressParam);

            userToGetInfoFor.setEmailAddresses(emailAdd);
        }

        if(this.serviceTicket != null)
        {
            userToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new User(this.postJson(
                    userToGetInfoFor, WS.Path.User.Version1.getByEmail()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves user information for the provided {@code userIdParam}.
     *
     * @param userIdParam The ID of the {@code User} to retrieve info for.
     * @return User information.
     *
     * @see User
     */
    public User getUserById(Long userIdParam)
    {
        User userToGetInfoFor = new User();
        userToGetInfoFor.setId(userIdParam);

        if(this.serviceTicket != null)
        {
            userToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new User(this.postJson(
                    userToGetInfoFor, WS.Path.User.Version1.getById()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves all user information.
     *
     * @return User information.
     *
     * @see UserListing
     */
    public UserListing getAllUsers()
    {
        UserListing userToGetInfoFor = new UserListing();

        if(this.serviceTicket != null)
        {
            userToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new UserListing(this.postJson(
                    userToGetInfoFor,
                    WS.Path.User.Version1.getAllUsers()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves all Users by {@code jobViewParam}.
     *
     * @return User information at {@code UserListing}
     *
     * @param jobViewParam The {@link JobView} to get users for.
     *
     * @see UserListing
     * @see JobView
     */
    public UserListing getAllUsersByJobView(JobView jobViewParam) {
        if(this.serviceTicket != null && jobViewParam != null) {
            jobViewParam.setServiceTicket(this.serviceTicket);
        }

        try {
            return new UserListing(this.postJson(
                    jobViewParam,
                    WS.Path.User.Version1.getAllUsersByJobView()));
        } catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves all Users by {@code roleParam}.
     *
     * @return User information at {@code UserListing}
     *
     * @param roleParam The {@link Role} to get users for.
     *
     * @see UserListing
     * @see Role
     */
    public UserListing getAllUsersByRole(Role roleParam) {
        if(this.serviceTicket != null && roleParam != null) {
            roleParam.setServiceTicket(this.serviceTicket);
        }

        try {
            return new UserListing(this.postJson(
                    roleParam, WS.Path.User.Version1.getAllUsersByRole()));
        } catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves all Users by {@code roleParam}.
     *
     * @return User information at {@code UserListing}
     *
     * @param loggedInSinceParam The date for last logged in.
     *
     * @see UserListing
     */
    public UserListing getAllUsersWhereLoggedInSince(Date loggedInSinceParam) {

        User userToPost = new User();
        userToPost.setLoggedInDateTime(loggedInSinceParam);

        if(this.serviceTicket != null) {
            userToPost.setServiceTicket(this.serviceTicket);
        }

        try {
            return new UserListing(this.postJson(
                    userToPost, WS.Path.User.Version1.getAllUsersWhereLoggedInSince()));
        } catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves all user field values information by the {@code userParam}.
     *
     * @param userParam The {@code User} to retrieve the field values for.
     * @return User information.
     *
     * @see UserFieldListing
     */
    public UserFieldListing getAllUserFieldValuesByUser(User userParam)
    {
        if(userParam == null)
        {
            return null;
        }

        if(this.serviceTicket != null)
        {
            userParam.setServiceTicket(this.serviceTicket);
        }

        try {
            return new UserFieldListing(this.postJson(
                    userParam,
                    WS.Path.User.Version1.getUserFieldValuesByUser()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieve the gravatar bytes by email.
     * The size will be 50x50.
     *
     * @param emailAddressParam The email to use for the gravatar.
     *
     * @return JPEG image bytes.
     */
    public byte[] getGravatarForEmail(
            String emailAddressParam)
    {
        return this.getGravatarForEmail(
                emailAddressParam,
                50);
    }

    /**
     * Retrieve the gravatar bytes by email.
     *
     * @param emailAddressParam The email to use for the gravatar.
     * @param sizeParam The pixel dimension for the image.
     *
     * @return JPEG image bytes.
     */
    public byte[] getGravatarForEmail(
            String emailAddressParam,
            int sizeParam)
    {
        try {
            JSONObject gravatarJSONObj =
                    this.getJson(
                            WS.Path.User.Version1.getGravatarByEmail(
                                    emailAddressParam, sizeParam));

            String base64Text = gravatarJSONObj.optString(JSON_TAG_DATA,"");
            if(base64Text == null || base64Text.isEmpty())
            {
                return null;
            }

            return UtilGlobal.decodeBase64(base64Text);
        }
        //JSON Parsing...
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    jsonExcept, FluidClientException.ErrorCode.JSON_PARSING);
        }
        //Encoding not supported...
        catch (UnsupportedEncodingException unsEncExcept) {
            throw new FluidClientException(unsEncExcept.getMessage(),
                    unsEncExcept, FluidClientException.ErrorCode.IO_ERROR);
        }
    }

    /**
     * Retrieve the gravatar bytes for Fluid user.
     * The size will be 50x50.
     *
     * @param userParam The user to get the gravatar for.
     *
     * @return JPEG image bytes.
     */
    public byte[] getGravatarForUser(User userParam)
    {
        return this.getGravatarForUser(userParam, 50);
    }

    /**
     * Retrieve the gravatar bytes for Fluid user.
     *
     * @param userParam The user to get the gravatar for.
     * @param sizeParam The pixel dimension for the image.
     *
     * @return JPEG image bytes.
     */
    public byte[] getGravatarForUser(User userParam, int sizeParam)
    {
        if(userParam == null)
        {
            return null;
        }

        try {
            JSONObject gravatarJSONObj = this.postJson(
                            userParam,
                            WS.Path.User.Version1.getGravatarByUser(sizeParam));

            String base64Text = gravatarJSONObj.optString(JSON_TAG_DATA,"");
            if(base64Text == null || base64Text.isEmpty())
            {
                return null;
            }

            return UtilGlobal.decodeBase64(base64Text);
        }
        //JSON problem...
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    jsonExcept, FluidClientException.ErrorCode.JSON_PARSING);
        }
    }
}