/******************************************************************************* * Copyright (c) 2013-2014 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.value.impl.persistent; import java.util.Comparator; import java.util.Iterator; import java.util.Map.Entry; import org.rascalmpl.value.IMap; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.impl.AbstractMap; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.util.AbstractTypeBag; import org.rascalmpl.value.util.EqualityUtils; import java.util.Objects; import io.usethesource.capsule.ImmutableMap; import io.usethesource.capsule.TransientMap; public final class PDBPersistentHashMap extends AbstractMap { @SuppressWarnings("unchecked") private static final Comparator<Object> equivalenceComparator = EqualityUtils.getEquivalenceComparator(); private Type cachedMapType; private final AbstractTypeBag keyTypeBag; private final AbstractTypeBag valTypeBag; private final ImmutableMap<IValue,IValue> content; /* * Passing an pre-calulated map type is only allowed from inside this class. */ protected PDBPersistentHashMap(AbstractTypeBag keyTypeBag, AbstractTypeBag valTypeBag, ImmutableMap<IValue, IValue> content) { Objects.requireNonNull(content); this.cachedMapType = null; this.keyTypeBag = keyTypeBag; this.valTypeBag = valTypeBag; this.content = content; } @Override protected IValueFactory getValueFactory() { return ValueFactory.getInstance(); } @Override public Type getType() { if (cachedMapType == null) { final Type keyType = keyTypeBag.lub(); final Type valType = valTypeBag.lub(); final String keyLabel = keyTypeBag.getLabel(); final String valLabel = valTypeBag.getLabel(); if (keyLabel != null && valLabel != null) { cachedMapType = getTypeFactory().mapType(keyType, keyLabel, valType, valLabel); } else { cachedMapType = getTypeFactory().mapType(keyType, valType); } } return cachedMapType; } @Override public boolean isEmpty() { return content.isEmpty(); } @Override public IMap put(IValue key, IValue value) { final ImmutableMap<IValue,IValue> contentNew = content.__putEquivalent(key, value, equivalenceComparator); if (content == contentNew) return this; final AbstractTypeBag keyBagNew; final AbstractTypeBag valBagNew; if (content.size() == contentNew.size()) { // value replaced final IValue replaced = content.getEquivalent(key, equivalenceComparator); keyBagNew = keyTypeBag; valBagNew = valTypeBag.decrease(replaced.getType()).increase(value.getType()); } else { // pair added keyBagNew = keyTypeBag.increase(key.getType()); valBagNew = valTypeBag.increase(value.getType()); } return new PDBPersistentHashMap(keyBagNew, valBagNew, contentNew); } @Override public int size() { return content.size(); } @Override public boolean containsKey(IValue key) { return content.containsKeyEquivalent(key, equivalenceComparator); } @Override public boolean containsValue(IValue value) { return content.containsValueEquivalent(value, equivalenceComparator); } @Override public IValue get(IValue key) { return content.getEquivalent(key, equivalenceComparator); } @Override public int hashCode() { return content.hashCode(); } @Override public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other instanceof PDBPersistentHashMap) { PDBPersistentHashMap that = (PDBPersistentHashMap) other; if (this.size() != that.size()) return false; return content.equals(that.content); } if (other instanceof IMap) { IMap that = (IMap) other; if (this.getType() != that.getType()) return false; if (this.size() != that.size()) return false; for (IValue e : that) { if (!content.containsKey(e)) { return false; } else if (!content.get(e).equals(that.get(e))) { return false; } } return true; } return false; } @Override public boolean isEqual(IValue other) { if (other == this) return true; if (other == null) return false; if (other instanceof IMap) { IMap that = (IMap) other; if (this.size() != that.size()) return false; for (IValue e : that) { if (!containsKey(e)) { return false; } else if (!get(e).isEqual(that.get(e))) { return false; } } return true; } return false; } @Override public Iterator<IValue> iterator() { return content.keyIterator(); } @Override public Iterator<IValue> valueIterator() { return content.valueIterator(); } @Override public Iterator<Entry<IValue, IValue>> entryIterator() { return content.entryIterator(); } @Deprecated private static String mergeLabels(String one, String two) { if (one != null && two != null && one.equals(two)) { // both are the same return one; } else { // only one is not null return one != null ? one : two; } } @Override public IMap join(IMap other) { if (other instanceof PDBPersistentHashMap) { PDBPersistentHashMap that = (PDBPersistentHashMap) other; final TransientMap<IValue, IValue> transientContent = content.asTransient(); boolean isModified = false; int previousSize = size(); AbstractTypeBag keyBagNew = null; if (that.keyTypeBag.getLabel() != null) { keyBagNew = keyTypeBag.setLabel(mergeLabels(keyTypeBag.getLabel(), that.keyTypeBag.getLabel())); isModified |= (keyBagNew.getLabel() != keyTypeBag.getLabel()); } else { keyBagNew = keyTypeBag; } AbstractTypeBag valBagNew = null; if (that.valTypeBag.getLabel() != null) { valBagNew = valTypeBag.setLabel(mergeLabels(valTypeBag.getLabel(), that.valTypeBag.getLabel())); isModified |= (valBagNew.getLabel() != valTypeBag.getLabel()); } else { valBagNew = valTypeBag; } for (Iterator<Entry<IValue, IValue>> it = that.entryIterator(); it.hasNext();) { Entry<IValue, IValue> tuple = it.next(); IValue key = tuple.getKey(); IValue value = tuple.getValue(); final IValue replaced = transientContent.__putEquivalent(key, value, equivalenceComparator); if (replaced != null) { // value replaced valBagNew = valBagNew.decrease(replaced.getType()).increase(value.getType()); isModified = true; } else if (previousSize != transientContent.size()) { // pair added keyBagNew = keyBagNew.increase(key.getType()); valBagNew = valBagNew.increase(value.getType()); isModified = true; previousSize++; } } if (isModified) { return new PDBPersistentHashMap(keyBagNew, valBagNew, transientContent.freeze()); } else { return this; } } else { return super.join(other); } } @Override public IMap remove(IMap that) { // TODO Auto-generated method stub return super.remove(that); } @Override public IMap compose(IMap that) { // TODO Auto-generated method stub return super.compose(that); } @Override public IMap common(IMap that) { // TODO Auto-generated method stub return super.common(that); } @Override public boolean isSubMap(IMap that) { // TODO Auto-generated method stub return super.isSubMap(that); } }