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

org.xacml4j.v30.BagOfAttributeExp Maven / Gradle / Ivy

The newest version!
package org.xacml4j.v30;

/*
 * #%L
 * Xacml4J Core Engine Implementation
 * %%
 * Copyright (C) 2009 - 2014 Xacml4J.org
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.io.Serializable;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Set;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.Multiset;

/**
 * XACML defines implicit collections of its data-types.
 * XACML refers to a collection of values that are of a single data-type as a bag.
 * Bags of data-types are needed because selections of nodes from an XML resource
 * or XACML request context may return more than one value.
 *
 * The values in a bag are not ordered, and some of the values may be duplicates.
 * There SHALL be no notion of a bag containing bags, or a bag containing values
 * of differing types. I.e. a bag in XACML SHALL contain only values that
 * are of the same data-type.
 *
 * @author Giedrius Trumpickas
 */
public final class BagOfAttributeExp
	implements ValueExpression, Serializable
{
	private static final long serialVersionUID = -8197446176793438616L;

	private final BagOfAttributeExpType type;
	private final Multiset values;
	private final int hashCode;

	/**
	 * Constructs bag of attributes.
	 *
	 * @param type a bag attribute type
	 * @param attributes a collection of attributes
	 */
	BagOfAttributeExp(BagOfAttributeExpType type,
			Iterable attributes){
		for (AttributeExp attr : attributes) {
			assertExpressionType(attr, type);
		}
		this.type = type;
		this.values = ImmutableMultiset.copyOf(attributes);
		this.hashCode = Objects.hashCode(type, values);
	}

	private BagOfAttributeExp(Builder b){
		this.type = b.bagType;
		this.values = b.valuesBuilder.build();
		this.hashCode = Objects.hashCode(type, values);
	}

	@SuppressWarnings("unchecked")
	public  Iterable values() {
		return (Iterable)values;
	}

	/**
	 * Gets bag value data type
	 *
	 * @return {@link AttributeExpType}
	 */
	public AttributeExpType getDataType(){
		return type.getDataType();
	}

	@Override
	public ValueType getEvaluatesTo() {
		return type;
	}

	/**
	 * Gets first attribute value
	 * in this bag
	 *
	 * @return first {@link AttributeExp}
	 * instance in this bag
	 * @exception NoSuchElementException if bag is empty
	 */
	public  T value(){
		return this.values().iterator().next();
	}

	/**
	 * Gets number of {@link AttributeExp}
	 * instances in this bag
	 *
	 * @return number of attributes
	 * in this bag
	 */
	public int size() {
		return values.size();
	}

	/**
	 * Tests if this bag is empty.
	 *
	 * @return {@code true} if bag is empty
	 */
	public boolean isEmpty(){
		return values.isEmpty();
	}

	/**
	 * Tests if this bag contains given attribute.
	 *
	 * @param attr an attribute
	 * @return {@code true} if bag contains given attribute
	 */
	public boolean contains(AttributeExp attr){
		return values.contains(attr);
	}

	/**
	 * Returns the number of elements in this bag equal
	 * to the specified attribute value.
	 *
	 * @param value an attribute value
	 * @return a number of elements equal to the
	 * specified value
	 */
	public int count(AttributeExp value){
		return values.count(value);
	}

	/**
	 * Returns a bag of such that it contains all elements of
	 * all the argument bags all duplicate elements are removed
	 *
	 * @param bag an another bag
	 * @return union of this and given bag without duplicate
	 * elements
	 */
	public BagOfAttributeExp union(BagOfAttributeExp bag)
	{
		Preconditions.checkArgument(type.equals(bag.type));
		Set union = new HashSet();
		union.addAll(bag.values);
		union.addAll(values);
		return type.create(union);
	}

	/**
	 * Returns a bag of values such that it contains only
	 * elements that are common between this and given bag
	 *
	 * @param bag an another bag
	 * @return bag which contains common
	 * elements between this and given bag
	 */
	public BagOfAttributeExp intersection(BagOfAttributeExp bag)
	{
		Preconditions.checkArgument(type.equals(bag.type));
		Set intersection = new HashSet();
		for(AttributeExp attr : values){
			if(bag.values.contains(attr)){
				intersection.add(attr);
			}
		}
		return type.create(intersection);
	}

	/**
	 * Tests if this bag contains at least on value
	 * from the given bag
	 *
	 * @param bag a bag
	 * @return {@code true} if this bag contains
	 * at least one value from the given bag
	 */
	public boolean containsAtLeastOneOf(BagOfAttributeExp bag)
	{
		for(AttributeExp v : bag.values){
			if(values.contains(v)){
				return true;
			}
		}
		return false;
	}

	@Override
	public BagOfAttributeExp evaluate(EvaluationContext context)
			throws EvaluationException {
		return this;
	}

	@Override
	public ValueType getType() {
		return type;
	}

	/**
	 * Tests if this bag is subset of given bag.
	 *
	 * @param bag a bag
	 * @return {@code true} if given bag
	 * is subset if this bag
	 */
	public boolean containsAll(BagOfAttributeExp bag){
		Preconditions.checkArgument(type.equals(bag.type));
		return values.containsAll(bag.values);
	}

	@Override
	public boolean equals(Object o){
		if(o == this){
			return true;
		}
		if(!(o instanceof BagOfAttributeExp)){
			return false;
		}
		BagOfAttributeExp bag = (BagOfAttributeExp)o;
		return type.equals(bag.type) &&
		values.equals(bag.values);
	}

	@Override
	public String toString() {
		return MoreObjects.toStringHelper(this).
		add("DataType", type.getDataType()).
		add("Values", values).toString();
	}

	@Override
	public int hashCode(){
		 return hashCode;
	}

	/**
	 * A static helper method to retrieve a single
	 * value from a given bag
	 *
	 * @param  attribute expression type
	 * @param v a bag of values
	 * @return a single value or {@code null}
	 * if a given bag is {@code null} or empty
	 */
	public static  T value(BagOfAttributeExp v){
		if(v == null ||
				v.isEmpty()){
			return null;
		}
		return v.value();
	}

	@Override
	public void accept(ExpressionVisitor expv) {
		BagOfAttributeVisitor v = (BagOfAttributeVisitor)expv;
		v.visitEnter(this);
		v.visitLeave(this);
	}

	public interface BagOfAttributeVisitor extends ExpressionVisitor
	{
		void visitEnter(BagOfAttributeExp v);
		void visitLeave(BagOfAttributeExp v);
	}

	private static void assertExpressionType(AttributeExp value, BagOfAttributeExpType bagType) {
		if (!value.getType().equals(bagType.getDataType())) {
			throw new IllegalArgumentException(String.format(
					"Given attribute value=\"%s\" " +
							"can't be used as a value of bag=\"%s\"", value, bagType));
		}
	}

	public static class Builder
	{

		private BagOfAttributeExpType bagType;
		private ImmutableMultiset.Builder valuesBuilder = ImmutableMultiset.builder();

		public Builder(AttributeExpType type){
			this.bagType = new BagOfAttributeExpType(type);
		}

		public Builder attribute(AttributeExp ...values){
			for(AttributeExp v : values){
				assertExpressionType(v, bagType);
				this.valuesBuilder.add(v);
			}
			return this;
		}

		public Builder value(Object ...values){
			for(Object v : values){
				this.valuesBuilder.add(bagType.getDataType().of(v));
			}
			return this;
		}

		public Builder values(Iterable values){
			for(Object v : values){
				this.valuesBuilder.add(bagType.getDataType().of(v));
			}
			return this;
		}

		public Builder attributes(Iterable values){
			for (AttributeExp v : values) {
				assertExpressionType(v, bagType);
			}
			this.valuesBuilder.addAll(values);
			return this;
		}

		public BagOfAttributeExp build(){
			return new BagOfAttributeExp(this);
		}
	}
}