/*
* Copyright 2003-2014 JetBrains s.r.o.
*
* 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 jetbrains.mps.smodel.persistence.def.v9;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModelReference;
import java.util.HashMap;
import java.util.Set;
/**
* Keeps information about model imports. Both write and read code use this to track
* model imports and their respective index values.
*
* The way model imports as well as node references utilizing these imports get serialized
* is external to this class, see {@link jetbrains.mps.smodel.persistence.def.v9.IdEncoder#toTextExternal(ImportsHelper, org.jetbrains.mps.openapi.model.SReference)}
` *
* Alternative to StorageIndexHelper9, index generation code (although questionable) is identical.
*/
class ImportsHelper {
private final SModelReference myModelRef;
private final HashMap<SModelReference, String> myModel2Index = new HashMap<SModelReference, String>();
private final HashMap<String, SModelReference> myIndex2Model = new HashMap<String, SModelReference>();
private static final int HASH_BASE = 10 + 26;
private static final int HASH_SIZE = HASH_BASE * HASH_BASE * HASH_BASE * HASH_BASE;
public ImportsHelper(@NotNull SModelReference model) {
myModelRef = model;
}
public String addModelImport(@NotNull SModelReference modelReference) {
if (myModel2Index.containsKey(modelReference)) {
// assert !myModel2Index.containsKey(modelReference);
Logger.getLogger(ImportsHelper.class).error(String.format("Model %s has duplicating import %s, ignored", myModelRef, modelReference));
return myModel2Index.get(modelReference);
}
String rv = createIndexFor(modelReference.getModelId().hashCode(), myIndex2Model.keySet());
register(rv, modelReference);
return rv;
}
public void addModelImport(@NotNull String index, @NotNull SModelReference modelReference) {
if (myIndex2Model.containsKey(index)) {
// throw new IllegalArgumentException(String.format...);
Logger.getLogger(ImportsHelper.class).error(String.format("Can't register import %s in model %s. Index %s is already in use for model %s", modelReference, myModelRef, index, myIndex2Model.get(index)));
return;
}
register(index, modelReference);
}
public String getIndex(@NotNull SModelReference modelReference) {
assert myModel2Index.containsKey(modelReference) : String.valueOf(modelReference);
return myModel2Index.get(modelReference);
}
public SModelReference getModelReference(@NotNull String index) {
assert myIndex2Model.containsKey(index);
return myIndex2Model.get(index);
}
// algorithm copied from StorageIndexHelper9.addInternalObject
/**
* Return value shall not use symbols other than [0-9][a-z] as the index is serialized as part of node identification,
* with {@link jetbrains.mps.smodel.persistence.def.v9.IdEncoder#REF_TARGET_IMPORT_SEPARATOR} as separator
*/
private String createIndexFor(int initialHash, Set<String> usedIndex) {
int hash = (initialHash % HASH_SIZE + HASH_SIZE) % HASH_SIZE;
String rv;
do {
rv = Integer.toString(hash, HASH_BASE);
hash = (hash + 1) % HASH_SIZE;
} while (usedIndex.contains(rv));
return rv;
}
private void register(String index, SModelReference modelReference) {
if (myModelRef.equals(modelReference)) {
// assert !myModelRef.equals(modelReference) : String.format("Model %s: no reason to keep imports to self", myModelRef);
Logger.getLogger(ImportsHelper.class).error(String.format("Model %s: no reason to keep imports to self", myModelRef));
}
myIndex2Model.put(index, modelReference);
myModel2Index.put(modelReference, index);
}
public boolean isLocal(SModelReference targetModel) {
return targetModel.equals(myModelRef);
}
public SModelReference localModel() {
return myModelRef;
}
}