/*
* Copyright 2003-2016 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.generator;
import jetbrains.mps.components.CoreComponent;
import jetbrains.mps.extapi.model.GeneratableSModel;
import jetbrains.mps.extapi.module.SRepositoryRegistry;
import jetbrains.mps.util.SNodeOperations;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.EditableSModel;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SRepositoryContentAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ModelGenerationStatusManager implements CoreComponent {
private static ModelGenerationStatusManager INSTANCE;
public static ModelGenerationStatusManager getInstance() {
return INSTANCE;
}
private final List<ModelGenerationStatusListener> myListeners = new ArrayList<ModelGenerationStatusListener>();
private ModelHashSource myModelHashSource;
private final SRepositoryContentAdapter myModelReloadListener = new SRepositoryContentAdapter() {
@Override
protected boolean isIncluded(SModule module) {
return !module.isPackaged() && !module.isReadOnly();
}
@Override
protected void startListening(SModel model) {
if (SNodeOperations.isGeneratable(model)) {
model.addModelListener(this);
}
}
@Override
protected void stopListening(SModel model) {
model.removeModelListener(this);
}
@Override
public void modelReplaced(SModel model) {
ModelGenerationStatusManager.this.invalidateData(Collections.singleton(model));
}
};
public ModelGenerationStatusManager() {
}
/*
* Now there could be only one source of model hashes at a time.
*/
public void setModelHashSource(@NotNull ModelHashSource source) {
myModelHashSource = source;
}
@Override
public void init() {
if (INSTANCE != null) {
throw new IllegalStateException("double initialization");
}
INSTANCE = this;
SRepositoryRegistry.getInstance().addGlobalListener(myModelReloadListener);
}
@Override
public void dispose() {
SRepositoryRegistry.getInstance().removeGlobalListener(myModelReloadListener);
INSTANCE = null;
}
public String currentHash(SModel md) {
if (md instanceof EditableSModel && ((EditableSModel)md).isChanged()) return null;
if (!(md instanceof GeneratableSModel)) return null;
return ((GeneratableSModel) md).getModelHash();
}
public boolean generationRequired(SModel md) {
if (!(md instanceof GeneratableSModel)) return false;
GeneratableSModel sm = (GeneratableSModel) md;
if (!sm.isGeneratable()) return false;
if (sm instanceof EditableSModel && ((EditableSModel) sm).isChanged()) return true;
String currentHash = sm.getModelHash();
if (currentHash == null) return true;
String generatedHash = getGenerationHash(sm);
if (generatedHash == null) return true;
return !generatedHash.equals(currentHash);
}
private String getGenerationHash(@NotNull GeneratableSModel sm) {
return getLastGenerationHash(sm);
}
/*
* REVISIT XXX whether SModelReference or SModel shall be in the API. Respect scenario when a file get changed
* and we have to update all model instances in all repositories (i.e. if the same model is loaded into few).
*/
public void invalidateData(Iterable<? extends SModel> models) {
ModelGenerationStatusListener[] copy;
synchronized (myListeners) {
copy = myListeners.toArray(new ModelGenerationStatusListener[myListeners.size()]);
}
for (SModel model : models) {
for (ModelGenerationStatusListener l : copy) {
l.generatedFilesChanged(model);
}
}
}
public void addGenerationStatusListener(ModelGenerationStatusListener l) {
synchronized (myListeners) {
myListeners.add(l);
}
}
public void removeGenerationStatusListener(ModelGenerationStatusListener l) {
synchronized (myListeners) {
myListeners.remove(l);
}
}
public static String getLastGenerationHash(GeneratableSModel sm) {
return String.valueOf(getInstance().myModelHashSource.getModelHash(sm));
}
/**
* PROVISIONAL, don't want a String as model hash, rather need a class.
*/
public interface ModelHashSource {
Object getModelHash(SModel model);
}
}