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

prompto.value.ListValue Maven / Gradle / Ivy

The newest version!
package prompto.value;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;

import prompto.error.IndexOutOfRangeError;
import prompto.error.PromptoError;
import prompto.error.ReadWriteError;
import prompto.error.SyntaxError;
import prompto.grammar.Identifier;
import prompto.intrinsic.Filterable;
import prompto.intrinsic.IterableWithCounts;
import prompto.intrinsic.PromptoList;
import prompto.intrinsic.PromptoSet;
import prompto.java.JavaClassType;
import prompto.runtime.Context;
import prompto.store.IStorable;
import prompto.store.InvalidValueError;
import prompto.type.AnyType;
import prompto.type.ContainerType;
import prompto.type.IType;
import prompto.type.ListType;

public class ListValue extends BaseValue implements IContainer, ISliceable, IFilterable  {

	PromptoList items;
	List storables;
	
	public ListValue(IType itemType) {
		this(itemType, new PromptoList<>(false));
	}
	
	public ListValue(IType itemType, PromptoList items) {
		this(itemType, items, items.isMutable());
	}

	public ListValue(IType itemType, Collection items) {
		this(itemType, new PromptoList<>(items, false), false);
	}
	
	public ListValue(IType itemType, Collection items, boolean mutable) {
		super(new ListType(itemType, mutable));
		this.items = items == null ? new PromptoList<>(mutable) : new PromptoList<>(items, mutable);
	}

	public ListValue(Context context, PromptoList list) {
		super(new ListType(AnyType.instance(), list.isMutable()));
		List items = list.stream()
				.map((item)->JavaClassType.convertJavaValueToPromptoValue(context, item, item.getClass(), AnyType.instance()))
				.collect(Collectors.toList());
		this.items = new PromptoList<>(items, false);
	}

	@Override
	public boolean isMutable() {
		return items.isMutable();
	}
	
	@Override
	public Object getStorableData() {
		if(storables==null) {
			storables = new ArrayList<>();
			for(IValue item : items)
				storables.add(item.getStorableData());
		}
		return storables;
	}
	
	@Override
	public void collectStorables(Consumer collector) {
		items.forEach((value)->
			value.collectStorables(collector));
	}
	
	@Override
	public String toString() {
		return items.toString();
	}

	public IType getItemType() {
		return ((ContainerType)type).getItemType();
	}

	public PromptoList getItems() {
		return items;
	}
	
	public void addItem(IValue item) {
		items.add(item);
	}
	
	public void removeItem(Long item) {
		items.remove(item.intValue() - 1);
	}

	public IValue getItem(int index) {
		return items.get(index);
	}
	
	public void setItem(int index, IValue element) {
		items.setValue(index, element);
	}
	
	public void removeValue(IValue value) {
		items.removeValue(value);
	}

	public void addValue(IValue value) {
		items.addValue(value);
	}

	public void insertValue(IValue value, long atIndex) {
		items.insertValue(value, atIndex);
	}

	public int indexOf(IValue value) {
		return items.indexOf(value);
	}

	@Override
	public void setItem(Context context, IValue item, IValue value) {
		if(!(item instanceof IntegerValue))
			throw new InvalidValueError("Expected an Integer, got:" + item.getClass().getName());
		int index = (int)((IntegerValue)item).longValue();
		if(index<1 || index>this.getLength())
			throw new IndexOutOfRangeError();
		this.setItem(index-1, value);
	}
	
	@Override
	public IValue getItem(Context context, IValue index) throws PromptoError {
		if (index instanceof IntegerValue) {
			try {
				int idx = (int)((IntegerValue)index).longValue() - 1;
				return items.get(idx);
			} catch (IndexOutOfBoundsException e) {
				throw new IndexOutOfRangeError();
			}

		} else
			throw new SyntaxError("No such item:" + index.toString());
	}

	@Override
	public boolean hasItem(Context context, IValue lval) throws PromptoError {
		return this.items.contains(lval); // TODO interpret before
	}

	@Override
	public long getLength() {
		return items.size();
	}

	@Override
	public Filterable getFilterable(Context context) {
		return new Filterable() {
			@Override
			public IValue filter(Predicate p) {
				PromptoList filtered = items.filter(p);
				return new ListValue(getItemType(), filtered);
			}
		};
	}
	
	@Override
	public IterableWithCounts getIterable(Context context) {
		return new IterableWithCounts() {
			@Override
			public Long getCount() {
				return (long)items.size();
			}
			@Override
			public Long getTotalCount() {
				return (long)items.size();
			}
			@Override
			public Iterator iterator() {
				return items.iterator();
			}
		};
	}
	
	@Override
	public IValue getMember(Context context, Identifier id, boolean autoCreate) {
		String name = id.toString();
		if ("count".equals(name))
			return new IntegerValue(items.size());
		else if ("iterator".equals(name))
			return new IteratorValue(getItemType(), getIterable(context).iterator());
		else
			return super.getMember(context, id, autoCreate);
	}
	
	@Override
	public IValue plus(Context context, IValue value) {
        if (value instanceof ListValue)
            return this.merge(((ListValue)value).getItems());
        else if (value instanceof SetValue)
            return this.merge(((SetValue)value).getItems());
        else
            throw new SyntaxError("Illegal: " + this.type.getTypeName() + " + " + value.getClass().getSimpleName());
    }
	
	protected ListValue merge(Collection items) {
		PromptoList result = new PromptoList(this.isMutable());
		result.addAll(this.items);
		result.addAll(items);
		IType itemType = ((ContainerType)getType()).getItemType();
		return new ListValue(itemType, result);
	}
	

	@Override
	public IValue minus(Context context, IValue value) throws PromptoError {
        if (value instanceof ListValue)
            return this.remove(((ListValue)value).getItems());
        else if (value instanceof SetValue)
            return this.remove(((SetValue)value).getItems());
        else
            throw new SyntaxError("Illegal: " + this.type.getTypeName() + " + " + value.getClass().getSimpleName());
	}
	

	protected ListValue remove(Collection items) {
		PromptoList result = new PromptoList(false);
		result.addAll(this.items);
		result.removeAll(items);
		IType itemType = ((ContainerType)getType()).getItemType();
		return new ListValue(itemType, result);
	}

	@Override
	public int hashCode() {
		return Objects.hash(items);
	}

	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof ListValue))
			return false;
		return items.equals(((ListValue)obj).items);
	}
	
	@Override
	public IValue multiply(Context context, IValue value) throws PromptoError {
		if (value instanceof IntegerValue) {
			IType itemType = ((ContainerType)this.type).getItemType();
			int count = (int) ((IntegerValue) value).longValue();
			if (count < 0)
				throw new SyntaxError("Negative repeat count:" + count);
			return new ListValue(itemType, this.items.multiply(count));
		} else
			throw new SyntaxError("Illegal: List * " + value.getClass().getSimpleName());
	}
	
	@Override
	public ListValue slice(IntegerValue fi, IntegerValue li) throws IndexOutOfRangeError {
		long _fi = fi == null ? 1L : fi.longValue();
		if (_fi < 0)
			throw new IndexOutOfRangeError();
		long _li = li == null ? items.size() : li.longValue();
		if (_li > items.size())
			throw new IndexOutOfRangeError();
		PromptoList sliced = items.slice(_fi, _li); // 1 based
		return new ListValue(this.getItemType(), sliced);
	}

	@Override
	public JsonNode valueToJsonNode(Context context, Function producer) throws PromptoError {
		ArrayNode result = JsonNodeFactory.instance.arrayNode();
		for(IValue item : items)
			result.add(producer.apply(item));
		return result;
	}

	
	@Override
	public void toJsonStream(Context context, JsonGenerator generator, boolean withType, Map data) throws PromptoError {
		try {
			if(withType) {
				generator.writeStartObject();
				generator.writeFieldName("type");
				generator.writeString(this.getType().getTypeName());
				generator.writeFieldName("value");
			}
			generator.writeStartArray();
			for(IValue value : this.items)
				value.toJsonStream(context, generator, withType, data);
			generator.writeEndArray();
			if(withType)
				generator.writeEndObject();
		} catch(IOException e) {
			throw new ReadWriteError(e.getMessage());
		}
	}
	
	@Override
	public Object toJavaValue(Context context, Type type) {
		if(canConvertTo(type)) {
			Type itemType = getItemType(type);
			PromptoList result = new PromptoList<>(true);
			items.forEach((item)->result.add(item.toJavaValue(context, itemType)));
			return result;
		} else
			return super.toJavaValue(context, type);
	}

	private boolean canConvertTo(Type type) {
		return type==PromptoList.class || (type instanceof Class && ((Class)type).isAssignableFrom(PromptoList.class));
	}

	private Type getItemType(Type type) {
		return type instanceof ParameterizedType ? ((ParameterizedType)type).getActualTypeArguments()[0] : Object.class;
	}

	public IValue toSetValue() {
		PromptoSet items = new PromptoSet<>(this.items);
		return new SetValue(((ListType)this.getType()).getItemType(), items);
	}

}