All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.getperka.flatpack.FlatPackEntity Maven / Gradle / Ivy

There is a newer version: 2.21.0
Show newest version
/*
 * #%L
 * FlatPack serialization code
 * %%
 * Copyright (C) 2012 Perka Inc.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.getperka.flatpack;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.inject.Inject;
import javax.validation.ConstraintViolation;

import org.joda.time.DateTime;

import com.getperka.flatpack.util.FlatPackCollections;
import com.getperka.flatpack.util.FlatPackTypes;
import com.google.inject.TypeLiteral;

/**
 * Encapsulates a return value, the role(s) to encode the entity for, and an optional collection of
 * extra entities to include in the payload.
 * 
 * @param  the type of value being returned
 */
public class FlatPackEntity extends TypeReference {
  /**
   * A convenience method to create a FlatPackEntity for a List of entities.
   * 
   * @param  the type of entity contained in the list.
   * @param clazz the entity type
   */
  public static  FlatPackEntity> collectionOf(
      final Class clazz) {
    return new FlatPackEntity>(new ParameterizedType() {
      @Override
      public Type[] getActualTypeArguments() {
        return new Type[] { clazz };
      }

      @Override
      public Type getOwnerType() {
        return null;
      }

      @Override
      public Type getRawType() {
        return Collection.class;
      }
    });
  }

  /**
   * A factory method to create a fully-specified FlatPackEntity.
   */
  public static FlatPackEntity create(Type returnType, Object toReturn, Principal principal) {
    // Convert primitive types to boxed
    Class erased = FlatPackTypes.erase(returnType);
    if (erased.isPrimitive()) {
      returnType = FlatPackTypes.box(erased);
    }
    return new FlatPackEntity(returnType).withPrincipal(principal).withValue(toReturn);
  }

  /**
   * A convenience method to create a FlatPackEntity that embeds a single entity as the return
   * value. This method is null-safe.
   */
  public static  FlatPackEntity entity(T toReturn) {
    return new FlatPackEntity(toReturn == null ? HasUuid.class : toReturn.getClass())
        .withValue(toReturn);
  }

  /**
   * A convenience method to create a FlatPackEntity for a Map of entities to entities.
   * 
   * @param  the type of entity for the map keys
   * @param  the type of entity for the map values
   * @param key the key entity type
   * @param value the value entity type
   */
  public static  FlatPackEntity> mapOf(
      final Class key, final Class value) {
    return new FlatPackEntity>(new ParameterizedType() {
      @Override
      public Type[] getActualTypeArguments() {
        return new Type[] { key, value };
      }

      @Override
      public Type getOwnerType() {
        return null;
      }

      @Override
      public Type getRawType() {
        return Map.class;
      }
    });
  }

  /**
   * Convenience method to return a FlatPackEntity that represents a null value. It is permissible
   * to call the extra decoration methods on the value returned from this method.
   */
  public static FlatPackEntity nullResponse() {
    return new FlatPackEntity(Void.class);
  }

  /**
   * A convenience method to create a FlatPackEntity for a Map of strings to entities.
   * 
   * @param  the type of entity for the map values
   * @param value the value entity type
   */
  public static  FlatPackEntity> stringMapOf(
      final Class value) {
    return new FlatPackEntity>(new ParameterizedType() {
      @Override
      public Type[] getActualTypeArguments() {
        return new Type[] { String.class, value };
      }

      @Override
      public Type getOwnerType() {
        return null;
      }

      @Override
      public Type getRawType() {
        return Map.class;
      }
    });
  }

  private DateTime lastModifiedTime;
  private Map extraData;
  private Set extraEntities;
  private Map extraErrors;
  private Map extraWarnings;
  private List metadata;
  private Principal principal;
  private TraversalMode traversalMode = TraversalMode.SIMPLE;
  private T value;
  private Set> violations;

  /**
   * Provide type information from implicit parameterization.
   */
  protected FlatPackEntity() {}

  /**
   * Allow construction via injection.
   */
  @Inject
  FlatPackEntity(TypeLiteral type) {
    super(type.getType());
  }

  /**
   * Only used by create methods.
   */
  private FlatPackEntity(Type type) {
    super(type);
  }

  /**
   * Populate the {@code errors} segment of the payload from {@link ConstraintViolation}.
   */
  public FlatPackEntity addConstraintViolations(Set> violations) {
    if (this.violations == null) {
      this.violations = FlatPackCollections.setForIteration();
    }
    this.violations.addAll(violations);
    return this;
  }

  /**
   * Add a key-value pair to the {@code errors} segment of the payload.
   */
  public FlatPackEntity addError(String key, String message) {
    if (extraErrors == null) {
      extraErrors = FlatPackCollections.mapForIteration();
    }
    extraErrors.put(key, message);
    return this;
  }

  /**
   * Add entities not normally reachable from {@link #getValue()} into the payload.
   */
  public FlatPackEntity addExtraEntities(Collection entities) {
    if (extraEntities == null) {
      extraEntities = FlatPackCollections.setForIteration();
    }
    extraEntities.addAll(entities);
    return this;
  }

  /**
   * Add an entity not normally reachable from {@link #getValue()} into the payload.
   */
  public FlatPackEntity addExtraEntity(HasUuid entity) {
    if (extraEntities == null) {
      extraEntities = FlatPackCollections.setForIteration();
    }
    extraEntities.add(entity);
    return this;
  }

  /**
   * Add a key-value pair to the {@code warnings} segment of the payload.
   */
  public FlatPackEntity addWarning(String key, String message) {
    if (extraWarnings == null) {
      extraWarnings = FlatPackCollections.mapForIteration();
    }
    extraWarnings.put(key, message);
    return this;
  }

  /**
   * Returns an immutable view of the ConstraintViolations that have been added to the
   * FlatPackEntity.
   */
  public Set> getConstraintViolations() {
    return violations == null ? Collections.> emptySet() :
        Collections.unmodifiableSet(violations);
  }

  /**
   * Returns an immutable view of the extra side-channel entries to include at the root of the
   * payload.
   */
  public Map getExtraData() {
    return extraData == null ? Collections. emptyMap() :
        Collections.unmodifiableMap(extraData);
  }

  /**
   * Returns an immutable view of the extra entities to include in the returned payload.
   */
  public Set getExtraEntities() {
    return extraEntities == null ? Collections. emptySet() :
        Collections.unmodifiableSet(extraEntities);
  }

  /**
   * Returns an immutable view of the extra errors to include in the returned payload.
   */
  public Map getExtraErrors() {
    return extraErrors == null ? Collections. emptyMap() :
        Collections.unmodifiableMap(extraErrors);
  }

  /**
   * Returns an immutable view of the extra warnings to include in the returned payload.
   */
  public Map getExtraWarnings() {
    return extraWarnings == null ? Collections. emptyMap() :
        Collections.unmodifiableMap(extraWarnings);
  }

  /**
   * Clients with some amount of temporary storage may request that entities that were created or
   * last modified before a particular instant in time be excluded from the data section.
   */
  public DateTime getLastModifiedTime() {
    return lastModifiedTime;
  }

  public Principal getPrincipal() {
    return principal;
  }

  public TraversalMode getTraversalMode() {
    return traversalMode;
  }

  /**
   * Returns the data previous passed to {@link #withValue(Object)}.
   */
  public T getValue() {
    return value;
  }

  /**
   * Allows extra string data to be returned at the top level of the payload.
   */
  public void putExtraData(String key, String value) {
    if (extraData == null) {
      extraData = new TreeMap();
    }
    extraData.put(key, value);
  }

  public FlatPackEntity withLastModifiedTime(DateTime lastModified) {
    this.lastModifiedTime = lastModified;
    return this;
  }

  public FlatPackEntity withPrincipal(Principal principal) {
    this.principal = principal;
    return this;
  }

  public FlatPackEntity withTraversalMode(TraversalMode traversalMode) {
    this.traversalMode = traversalMode;
    return this;
  }

  public FlatPackEntity withValue(T value) {
    // Sanity-check type assignments
    if (value != null && !FlatPackTypes.erase(getType()).isInstance(value)) {
      throw new IllegalArgumentException(value.getClass().getName() + " is not assignable to "
        + getType());
    }
    this.value = value;
    return this;
  }

  FlatPackEntity addMetadata(EntityMetadata meta) {
    if (metadata == null) {
      metadata = FlatPackCollections.listForAny();
    }
    metadata.add(meta);
    return this;
  }

  List getMetadata() {
    return metadata == null ? Collections. emptyList() :
        Collections.unmodifiableList(metadata);
  }

  void setExtraEntities(Set extraEntities) {
    this.extraEntities = extraEntities;
  }
}