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

prompto.value.DictionaryValue 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.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Map.Entry;
import java.util.function.Function;

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

import prompto.error.PromptoError;
import prompto.error.ReadWriteError;
import prompto.error.SyntaxError;
import prompto.grammar.Identifier;
import prompto.intrinsic.IterableWithCounts;
import prompto.intrinsic.PromptoDict;
import prompto.intrinsic.PromptoSet;
import prompto.runtime.Context;
import prompto.store.InvalidValueError;
import prompto.type.ContainerType;
import prompto.type.DictType;
import prompto.type.IType;
import prompto.type.TextType;

public class DictionaryValue extends BaseValue implements IContainer {

	PromptoDict dict;
	
	public DictionaryValue(IType itemType, boolean mutable) {
		super(new DictType(itemType));
		dict = new PromptoDict<>(mutable);
	}

	public DictionaryValue(DictionaryValue from) {
		this(((ContainerType) from.type).getItemType(), from.dict);
	}

	public DictionaryValue(IType itemType, PromptoDict dict) {
		super(new DictType(itemType));
		this.dict = dict;
	}
	
	@Override
	public PromptoDict getStorableData() {
		return dict;
	}

	public static DictionaryValue merge(DictionaryValue dict1, DictionaryValue dict2) {
		PromptoDict dict = new PromptoDict<>(false);
		dict.putAll(dict1.dict);
		dict.putAll(dict2.dict);
		// TODO check type fungibility		
		return new DictionaryValue(((ContainerType) dict1.type).getItemType(), dict); 
	}

	public DictionaryValue swap(Context context) {
		Identifier text = new Identifier("text"); 
		PromptoDict swapped = new PromptoDict<>(true);
		dict.forEach((k,v)->{
			if(!(v instanceof TextValue))
				v = v.getMember(context, text, false);
			swapped.put((TextValue)v, k);
		});
		swapped.setMutable(false);
		return new DictionaryValue(TextType.instance(), swapped);
	}

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

	@Override
	public IValue plus(Context context, IValue value) throws PromptoError {
		if (value instanceof DictionaryValue)
			return merge(this, (DictionaryValue) value);
		else
			throw new SyntaxError("Illegal: Dict + " + value.getClass().getSimpleName());
	}
	
	@Override
	public boolean hasItem(Context context, IValue value) {
		if (value instanceof TextValue)
			return this.dict.containsKey((TextValue) value);
		else
			throw new SyntaxError("Only Text key supported by " + this.getClass().getSimpleName());
	}

	@Override
	public boolean isMutable() {
		return this.dict.isMutable();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public IValue getMember(Context context, Identifier id, boolean autoCreate) throws PromptoError {
		String name = id.toString();
		switch(name) {
		case "count":
			return new IntegerValue(this.dict.size());
		case "keys":
			return new SetValue(TextType.instance(), (PromptoSet)(Object)new PromptoSet(this.dict.keySet()));
		case "values":
			return new ListValue(((ContainerType) this.type).getItemType(), this.dict.values());
		case "json":
			// fallthrough
		default:
			return super.getMember(context, id, autoCreate);
		}
	}
	
	
	
	@Override
	public void setItem(Context context, IValue item, IValue value) {
		if(!(item instanceof TextValue))
			throw new InvalidValueError("Expected a Text, got:" + item.getClass().getName());
		this.dict.put((TextValue)item, value);
	}

	@Override
	public IValue getItem(Context context, IValue index) throws PromptoError {
		return getItem(index);
	}
	
	
	public IValue getItem(IValue index) throws PromptoError {
		if (index instanceof TextValue)
			return dict.getOrDefault((TextValue) index, NullValue.instance());
		else
			throw new SyntaxError("No such item:" + index.toString());
	}


	@Override
	public Object toJavaValue(Context context, Type type) {
		if(canConvertTo(type)) {
			Type itemType = getItemType(type);
			PromptoDict dict = new PromptoDict<>(true);
			for(Map.Entry entry : this.dict.entrySet()) {
				String key = entry.getKey().toString();
				Object value = entry.getValue().toJavaValue(context, itemType);
				dict.put(key, value);
			}
			dict.setMutable(this.isMutable());
			return dict;
		} else
			return super.toJavaValue(context, type);
	}

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

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

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

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof DictionaryValue))
			return false;
		return dict.equals(((DictionaryValue) obj).dict);
	}

	@Override
	public String toString() {
		return dict.toString();
	}
	
	@Override
	public JsonNode valueToJsonNode(Context context, Function producer) throws PromptoError {
		ObjectNode result = JsonNodeFactory.instance.objectNode();
		for(Entry entry : dict.entrySet())
			result.set(entry.getKey().getStorableData(), producer.apply(entry.getValue()));
		return result;
	}
	
	
	@Override
	public void toJsonStream(Context context, JsonGenerator generator, boolean withType, Map binaries) throws PromptoError {
		try {
			if(withType) {
				generator.writeStartObject();
				generator.writeFieldName("type");
				generator.writeString(this.getType().getTypeName());
				generator.writeFieldName("value");
			}
			generator.writeStartObject();
			for(Entry entry : dict.entrySet()) {
				generator.writeFieldName(entry.getKey().toString());
				IValue value = entry.getValue();
				if(value==null)
					generator.writeNull();
				else
					value.toJsonStream(context, generator, withType, binaries);
			}
			generator.writeEndObject();
			if(withType)
				generator.writeEndObject();
		} catch(IOException e) {
			throw new ReadWriteError(e.getMessage());
		}
	}


	@Override
	public IterableWithCounts getIterable(Context context) {
		return new KVPIterable(context);
	}

	class KVPIterable implements IterableWithCounts {

		Context context;

		public KVPIterable(Context context) {
			this.context = context;
		}

		@Override
		public Long getCount() {
			return (long)dict.size();
		}
		
		@Override
		public Long getTotalCount() {
			return (long)dict.size();
		}
		
		@Override
		public Iterator iterator() {
			return new Iterator() {

				Iterator> iterator = dict.entrySet().iterator();
				
				@Override
				public boolean hasNext() {
					return iterator.hasNext();
				}
	
				@Override
				public KVPValue next() {
					return new KVPValue(iterator.next());
				}
	
				@Override
				public void remove() {
					iterator.remove();
				}
			};

		}

	}

	static class KVPValue extends BaseValue {
		Entry kvp;

		public KVPValue(Entry kvp) {
			super(null); // TODO, check that this is not a problem
			this.kvp = kvp;
		}

		@Override
		public IValue getMember(Context context, Identifier id, boolean autoCreate) throws PromptoError {
			String name = id.toString();
			if ("key".equals(name))
				return kvp.getKey();
			else if ("value".equals(name))
				return kvp.getValue();
			else
				throw new SyntaxError("No such member:" + name);
		}
		
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy