/*
* Copyright 2003-2015 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;
import jetbrains.mps.RuntimeFlags;
import jetbrains.mps.logging.Logger;
import jetbrains.mps.smodel.adapter.structure.ref.SReferenceLinkAdapterById;
import jetbrains.mps.smodel.language.ConceptRegistryUtil;
import jetbrains.mps.smodel.legacy.ConceptMetaInfoConverter;
import jetbrains.mps.smodel.runtime.ConstraintsDescriptor;
import jetbrains.mps.smodel.runtime.PropertyConstraintsDescriptor;
import jetbrains.mps.smodel.runtime.ReferenceConstraintsDescriptor;
import jetbrains.mps.util.Pair;
import org.apache.log4j.LogManager;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeAccessUtil;
import java.util.HashSet;
import java.util.Set;
public class SNodeAccessUtilImpl extends SNodeAccessUtil {
private static Logger LOG = Logger.wrap(LogManager.getLogger(SNodeAccessUtil.class));
//SNodeAccessUtilImpl has only one instance, so we can omit remove() here though the field is not static
private final ThreadLocal<Set<Pair<org.jetbrains.mps.openapi.model.SNode, SProperty>>> ourPropertySettersInProgress = new InProgressThreadLocal<SProperty>();
private final ThreadLocal<Set<Pair<org.jetbrains.mps.openapi.model.SNode, SProperty>>> ourPropertyGettersInProgress = new InProgressThreadLocal<SProperty>();
private final ThreadLocal<Set<Pair<org.jetbrains.mps.openapi.model.SNode, SReferenceLink>>> ourSetReferentEventHandlersInProgress =
new InProgressThreadLocal<SReferenceLink>();
@Override
protected boolean hasPropertyImpl(org.jetbrains.mps.openapi.model.SNode node, SProperty property) {
node.hasProperty(property); //todo this is to invoke corresponding read access. try to remove it by merging 2 types of access
String property_internal = node.getProperty(property);
return !SModelUtil_new.isEmptyPropertyValue(property_internal);
}
@Override
protected boolean hasPropertyImpl(org.jetbrains.mps.openapi.model.SNode node, String name) {
return hasPropertyImpl(node, ((ConceptMetaInfoConverter) node.getConcept()).convertProperty(name));
}
@Override
public String getPropertyImpl(org.jetbrains.mps.openapi.model.SNode node, SProperty property) {
if (RuntimeFlags.isMergeDriverMode()) return node.getProperty(property);
Set<Pair<SNode, SProperty>> getters = ourPropertyGettersInProgress.get();
Pair<SNode, SProperty> current = new Pair<SNode, SProperty>(node, property);
if (getters.contains(current)) return node.getProperty(property);
getters.add(current);
try {
PropertyConstraintsDescriptor descriptor = PropertySupport.getPropertyConstraintsDescriptor(node, property);
Object getterValue = descriptor != null ? descriptor.getValue(node) : node.getProperty(property);
return getterValue == null ? null : String.valueOf(getterValue);
} catch (Throwable t) {
LOG.error(t);
return null;
} finally {
getters.remove(current);
}
}
private static ReferenceConstraintsDescriptor getReferenceConstraintsDescriptor(SNode node, SReferenceLink referenceLink) {
ConstraintsDescriptor constraintsDescriptor = ConceptRegistryUtil.getConstraintsDescriptor(node.getConcept());
ReferenceConstraintsDescriptor descriptor;
descriptor = constraintsDescriptor.getReference(referenceLink);
return descriptor;
}
@Override
public String getPropertyImpl(org.jetbrains.mps.openapi.model.SNode node, String name) {
return getPropertyImpl(node, ((ConceptMetaInfoConverter) node.getConcept()).convertProperty(name));
}
@Override
public void setPropertyImpl(org.jetbrains.mps.openapi.model.SNode node, SProperty property, String propertyValue) {
Set<Pair<SNode, SProperty>> threadSet = ourPropertySettersInProgress.get();
Pair<SNode, SProperty> pair = new Pair<SNode, SProperty>(node, property);
//todo try to remove
if (threadSet.contains(pair)) {
node.setProperty(property, propertyValue);
return;
}
PropertyConstraintsDescriptor descriptor = PropertySupport.getPropertyConstraintsDescriptor(node, property);
threadSet.add(pair);
try {
if (descriptor != null) {
descriptor.setValue(node, propertyValue);
} else {
LOG.error(
"Can't find property constraints for property `" + property.getName() + "`. Setting directly. Value: `" + propertyValue + "`.", node);
node.setProperty(property, propertyValue);
}
} catch (Exception t) {
LOG.error(t);
} finally {
threadSet.remove(pair);
}
}
@Override
public void setPropertyImpl(org.jetbrains.mps.openapi.model.SNode node, String propertyName, String propertyValue) {
setPropertyImpl(node, ((ConceptMetaInfoConverter) node.getConcept()).convertProperty(propertyName), propertyValue);
}
@Override
public void setReferenceTargetImpl(org.jetbrains.mps.openapi.model.SNode node, SReferenceLink referenceLink,
@Nullable org.jetbrains.mps.openapi.model.SNode target) {
// invoke custom referent set event handler
Set<Pair<SNode, SReferenceLink>> threadSet = ourSetReferentEventHandlersInProgress.get();
Pair<SNode, SReferenceLink> pair = new Pair<SNode, SReferenceLink>(node, referenceLink);
if (threadSet.contains(pair)) {
node.setReferenceTarget(referenceLink, target);
return;
}
ReferenceConstraintsDescriptor descriptor = getReferenceConstraintsDescriptor(node, referenceLink);
if (descriptor == null) {
LOG.error(
"Can't find reference constraints for reference `" + referenceLink.getRoleName() + "`. Setting directly.", node);
node.setReferenceTarget(referenceLink, target);
return;
}
threadSet.add(pair);
try {
org.jetbrains.mps.openapi.model.SReference r = node.getReference(referenceLink);
org.jetbrains.mps.openapi.model.SNode oldReferent = r == null ? null : SReference.getTargetNodeSilently(r);
if (descriptor.validate(node, oldReferent, target)) {
node.setReferenceTarget(referenceLink, target);
descriptor.onReferenceSet(node, oldReferent, target);
}
} finally {
threadSet.remove(pair);
}
}
@Override
public void setReferenceTargetImpl(org.jetbrains.mps.openapi.model.SNode node, String role, @Nullable org.jetbrains.mps.openapi.model.SNode target) {
setReferenceTargetImpl(node, ((ConceptMetaInfoConverter) node.getConcept()).convertAssociation(role), target);
}
@Override
public void setReferenceImpl(org.jetbrains.mps.openapi.model.SNode node, SReferenceLink referenceLink,
@Nullable org.jetbrains.mps.openapi.model.SReference reference) {
//todo for symmetry.Not yet used
node.setReference(referenceLink, reference);
}
public void setReferenceImpl(org.jetbrains.mps.openapi.model.SNode node, String role, @Nullable org.jetbrains.mps.openapi.model.SReference reference) {
setReferenceImpl(node, ((ConceptMetaInfoConverter) node.getConcept()).convertAssociation(role), reference);
}
private class InProgressThreadLocal<T> extends ThreadLocal<Set<Pair<org.jetbrains.mps.openapi.model.SNode, T>>> {
@Override
protected Set<Pair<org.jetbrains.mps.openapi.model.SNode, T>> initialValue() {
return new HashSet<Pair<SNode, T>>();
}
}
}