/************************************************************************
* Copyright (c) 2014-2015 IoT-Solutions e.U.
*
* 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.
************************************************************************/
package iot.jcypher.domain.mapping.surrogate;
import iot.jcypher.domain.mapping.DomainState;
import iot.jcypher.domain.mapping.DomainState.IRelation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class SurrogateState {
private Map<Surrogate_Key, ReferredSurrogate<?>> map2SurrogateMap;
public SurrogateState() {
super();
this.map2SurrogateMap = new HashMap<Surrogate_Key, ReferredSurrogate<?>>();
}
public SurrogateState createCopy(Map<IRelation, IRelation> copiedRels,
DomainState ds) {
SurrogateState ret = new SurrogateState();
Map<Surrogate_Key, Surrogate_Key> copiedSurrks = new IdentityHashMap<Surrogate_Key, Surrogate_Key>();
Map<ReferredSurrogate<?>, ReferredSurrogate<?>> copiedRefSurrs =
new IdentityHashMap<ReferredSurrogate<?>, ReferredSurrogate<?>>();
Iterator<Entry<Surrogate_Key, ReferredSurrogate<?>>> it = this.map2SurrogateMap.entrySet().iterator();
while(it.hasNext()) {
Entry<Surrogate_Key, ReferredSurrogate<?>> entry = it.next();
ret.map2SurrogateMap.put(copySurrks(entry.getKey(), copiedSurrks),
copyRefSurr(entry.getValue(), copiedRefSurrs, copiedRels, ds));
}
return ret;
}
private ReferredSurrogate<?> copyRefSurr(ReferredSurrogate<?> toCopy,
Map<ReferredSurrogate<?>, ReferredSurrogate<?>> copiedRefSurrs,
Map<IRelation, IRelation> copiedRels,
DomainState ds) {
ReferredSurrogate<?> ret = copiedRefSurrs.get(toCopy);
if (ret == null) {
ret = new ReferredSurrogate<>(toCopy.surrogate);
for (IRelation rel : toCopy.references) {
IRelation crel = copyRelation(rel, copiedRels, ds);
ret.references.add(crel);
}
copiedRefSurrs.put(toCopy, ret);
}
return ret;
}
@SuppressWarnings("unchecked")
private <T extends IRelation> T copyRelation(T toCopy, Map<IRelation, IRelation> copiedRels,
DomainState ds) {
T crel = (T) copiedRels.get(toCopy);
if (crel == null) {
crel = (T) toCopy.createCopy(ds);
copiedRels.put(toCopy, crel);
}
return crel;
}
private Surrogate_Key copySurrks(Surrogate_Key toCopy,
Map<Surrogate_Key, Surrogate_Key> copiedSurrks) {
Surrogate_Key ret = copiedSurrks.get(toCopy);
if (ret == null) {
ret = new Surrogate_Key(toCopy.original);
copiedSurrks.put(toCopy, ret);
}
return ret;
}
private <T extends AbstractSurrogate> void addOriginal2ReferredSurrogate(Object original, ReferredSurrogate<T> refMap) {
this.map2SurrogateMap.put(new Surrogate_Key(original), refMap);
}
@SuppressWarnings("rawtypes")
private ReferredSurrogate getReferredSurrogate (Object original) {
return this.map2SurrogateMap.get(new Surrogate_Key(original));
}
@SuppressWarnings("unchecked")
public <T extends AbstractSurrogate> void addOriginal2Surrogate(Object original, T surrogate) {
ReferredSurrogate<T> refSurrogate = getReferredSurrogate(original);
if (refSurrogate != null && refSurrogate.getSurrogate() != surrogate)
throw new RuntimeException("error existing surrogate map");
if (refSurrogate == null) {
refSurrogate = new ReferredSurrogate<T>(surrogate);
addOriginal2ReferredSurrogate(original, refSurrogate);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T extends AbstractSurrogate> T getCreateSurrogateFor (Object original,
Class<T> surrogateClass) {
ReferredSurrogate<T> refSurrogate = getReferredSurrogate(original);
if (refSurrogate == null) {
refSurrogate = new ReferredSurrogate(AbstractSurrogate.createSurrogate(original));
addOriginal2ReferredSurrogate(original, refSurrogate);
}
return refSurrogate.getSurrogate();
}
public void addReference(IRelation ref) {
Object target = ref.getEnd();
if (target instanceof AbstractSurrogate) {
ReferredSurrogate<?> refSurrogate = getReferredSurrogate(
((AbstractSurrogate)target).getContent());
if (refSurrogate != null) {
refSurrogate.addReference(ref);
}
}
}
public void removeReference(IRelation ref) {
Object target = ref.getEnd();
if (target instanceof AbstractSurrogate) {
ReferredSurrogate<?> refSurrogate = getReferredSurrogate(
((AbstractSurrogate)target).getContent());
if (refSurrogate != null) {
refSurrogate.removeReference(ref);
}
}
}
public void removeUnreferenced() {
List<Surrogate_Key> toRemove = new ArrayList<Surrogate_Key>();
Iterator<Entry<Surrogate_Key, ReferredSurrogate<?>>> it = this.map2SurrogateMap.entrySet().iterator();
while(it.hasNext()) {
Entry<Surrogate_Key, ReferredSurrogate<?>> entry = it.next();
if (entry.getValue().getReferenceCount() == 0)
toRemove.add(entry.getKey());
}
for(Surrogate_Key k : toRemove) {
this.map2SurrogateMap.remove(k);
}
}
public int size() {
return this.map2SurrogateMap.size();
}
/********************************/
public static class Surrogate_Key {
private Object original;
private Surrogate_Key(Object org) {
super();
this.original = org;
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Surrogate_Key))
return false;
Surrogate_Key e = (Surrogate_Key)o;
Object k1 = this.original;
Object k2 = e.original;
return k1 == k2;
}
@Override
public final int hashCode() {
// cannot return the hashcode of the map or collection
// as it changes when the content of the map or collection changes
// this occurs during filling the map or collection
// TODO find a better solution
if (this.original instanceof Map<?, ?>)
return 0;
else
return 1;
}
}
/********************************/
public static class ReferredSurrogate<T extends AbstractSurrogate> {
// the surrogate is either a surrogate.Map or a surrogate.Collection
private T surrogate;
private List<IRelation> references;
public ReferredSurrogate(T surrogate) {
super();
this.surrogate = surrogate;
this.references = new ArrayList<IRelation>();
}
public T getSurrogate() {
return this.surrogate;
}
public void addReference(IRelation reference) {
if (!references.contains(reference))
references.add(reference);
}
public void removeReference(IRelation reference) {
references.remove(reference);
}
public int getReferenceCount() {
return references.size();
}
}
}