/*
* 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.smodel.runtime.impl;
import jetbrains.mps.smodel.SNodePointer;
import jetbrains.mps.smodel.adapter.ids.MetaIdFactory;
import jetbrains.mps.smodel.adapter.ids.SConceptId;
import jetbrains.mps.smodel.adapter.ids.SLanguageId;
import jetbrains.mps.smodel.runtime.BaseLinkDescriptor;
import jetbrains.mps.smodel.runtime.BasePropertyDescriptor;
import jetbrains.mps.smodel.runtime.BaseReferenceDescriptor;
import jetbrains.mps.smodel.runtime.ConceptDescriptor;
import jetbrains.mps.smodel.runtime.ConceptKind;
import jetbrains.mps.smodel.runtime.LinkDescriptor;
import jetbrains.mps.smodel.runtime.PropertyDescriptor;
import jetbrains.mps.smodel.runtime.ReferenceDescriptor;
import jetbrains.mps.smodel.runtime.StaticScope;
import jetbrains.mps.util.NameUtil;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;
import java.util.ArrayList;
/**
* FIXME why j.m.smodel.runtime is part of [kernel] (especially semi-API part like ConceptKind)
* @author Artem Tikhomirov
*/
public class ConceptDescriptorBuilder2 {
private int myVersion = 1;
private final String myConceptShortName;
private final String myLanguageName;
private final long myLanguageHighUUID;
private final long myLanguageLowUUID;
/*package*/ final SConceptId myConceptId;
private SConceptId mySuperConceptId;
private String mySuperConceptQualifiedName;
private boolean myIsFinal;
private boolean myIsAbstract;
private boolean myIsInterface;
private boolean myIsRoot;
private ConceptKind myConceptKind = ConceptKind.NORMAL;
private StaticScope myScope = StaticScope.GLOBAL;
private String myAlias;
/*package*/ SNodeReference myOrigin;
private ArrayList<SConceptId> myParents = new ArrayList<>(4);
private ArrayList<PropertyDescriptor> myProperties;
private ArrayList<ReferenceDescriptor> myAssociations;
private ArrayList<LinkDescriptor> myAggregations;
// arguments != null
public ConceptDescriptorBuilder2(String languageName, String conceptShortName, long langIdHigh, long langIdLow, long conceptId) {
myLanguageName = languageName;
myLanguageHighUUID = langIdHigh;
myLanguageLowUUID = langIdLow;
myConceptShortName = conceptShortName;
myConceptId = MetaIdFactory.conceptId(myLanguageHighUUID, myLanguageLowUUID, conceptId);
}
/*
* invoked [0..1] times
*/
public ConceptDescriptorBuilder2 super_(String conceptQualifiedName, long langIdHigh, long langIdLow, long conceptId) {
// no need to specify name of superconcept, it's not in use
mySuperConceptQualifiedName = conceptQualifiedName;
mySuperConceptId = MetaIdFactory.conceptId(languageId(langIdHigh, langIdLow), conceptId);
return this;
}
/*
* invoked [0..n] times
*/
public ConceptDescriptorBuilder2 parent(long langIdHigh, long langIdLow, long conceptId) {
myParents.add(MetaIdFactory.conceptId(languageId(langIdHigh, langIdLow), conceptId));
return this;
}
public ConceptDescriptorBuilder2 origin(SNodeReference origin) {
myOrigin = origin;
return this;
}
/*
* String representation of a node that served as origin for the concept, facilitates backlink to source code and
* NOW helps to find out {@code node<AbstractConceptDeclaration>} for an SConcept.
*/
public ConceptDescriptorBuilder2 origin(String originReference) {
myOrigin = PersistenceFacade.getInstance().createNodeReference(originReference);
return this;
}
/*
* invoked [0..n] times
*/
public ConceptDescriptorBuilder2 prop(String name, long propertyId) {
addProperty(new BasePropertyDescriptor(MetaIdFactory.propId(myConceptId, propertyId), name, null));
return this;
}
/*
* invoked [0..n] times
* @param sourceNodeId as long as PropertyDeclaration doesn't have sourceNode reference, we use model of declared concept as
* source model and PD's node id to identify its source node, hence the parameter that takes only relevant part (no reason
* to pass model reference again and again).
*
*/
public ConceptDescriptorBuilder2 prop(String name, long propertyId, String sourceNodeId) {
SNodeReference srcNode = myOrigin != null && sourceNodeId != null ? new SNodePointer(myOrigin.getModelReference(), PersistenceFacade.getInstance().createNodeId(sourceNodeId)) : null;
addProperty(new BasePropertyDescriptor(MetaIdFactory.propId(myConceptId, propertyId), name, srcNode));
return this;
}
public ConceptDescriptorBuilder2 prop(String name, long propertyId, SNodeReference srcNode) {
myProperties.add(new BasePropertyDescriptor(MetaIdFactory.propId(myConceptId, propertyId), name, srcNode));
// perhaps, propertySourceNode(long,SNodeReference) instead of duplicated method? Same for optional(linkId) value?
return this;
}
/*
* invoked [0..n] times
*/
public AssociationLinkBuilder associate(String name, long associationLinkId) {
return new AssociationLinkBuilder(this, name, associationLinkId);
}
/*
* invoked [0..n] times
*/
public AggregationLinkBuilder aggregate(String name, long aggregationLinkId) {
return new AggregationLinkBuilder(this, name, aggregationLinkId);
}
public ConceptDescriptorBuilder2 interface_() {
myIsFinal = false;
myIsAbstract = myIsInterface = true;
return this;
}
public ConceptDescriptorBuilder2 class_(boolean isFinal, boolean isAbstract) {
// likely, root property would move away from structure descriptor, hence introduced proper method right away
myIsFinal = isFinal;
myIsAbstract = isAbstract;
myIsInterface = false;
return this;
}
public ConceptDescriptorBuilder2 class_(boolean isFinal, boolean isAbstract, boolean root) {
class_(isFinal, isAbstract);
myIsRoot = root;
return this;
}
// optional, version == 1 by default as this builder was introduced late in 3.4 cycle
public ConceptDescriptorBuilder2 version(int version) {
myVersion = version;
return this;
}
public ConceptDescriptorBuilder2 kind(ConceptKind conceptKind, StaticScope scope) {
myConceptKind = conceptKind;
myScope = scope;
return this;
}
// optional
public ConceptDescriptorBuilder2 alias(String alias) {
// why shortDescription is part of presentation aspect, while alias is not?
myAlias = alias;
return this;
}
public ConceptDescriptor create() {
String conceptFQName = NameUtil.conceptFQNameFromNamespaceAndShortName(myLanguageName, myConceptShortName);
PropertyDescriptor[] pd = myProperties == null ? new PropertyDescriptor[0] : myProperties.toArray(new PropertyDescriptor[myProperties.size()]);
ReferenceDescriptor[] assoc = myAssociations == null ? new ReferenceDescriptor[0] : myAssociations.toArray(new ReferenceDescriptor[myAssociations.size()]);
LinkDescriptor[] aggr = myAggregations == null ? new LinkDescriptor[0] : myAggregations.toArray(new LinkDescriptor[myAggregations.size()]);
SConceptId[] parents = myParents.toArray(new SConceptId[myParents.size()]);
return new CompiledConceptDescriptor(myVersion, myConceptId, conceptFQName, mySuperConceptId, mySuperConceptQualifiedName, myIsInterface, parents, null, pd, assoc, aggr, myIsAbstract, myIsFinal, myIsRoot, myAlias, null, null, myScope, /* FIXME myConceptKind, */myOrigin);
}
/*package*/ void addProperty(PropertyDescriptor d) {
if (myProperties == null) {
myProperties = new ArrayList<>(6);
}
myProperties.add(d);
}
/*package*/ void addAssociation(ReferenceDescriptor d) {
if (myAssociations == null) {
myAssociations = new ArrayList<>(4);
}
myAssociations.add(d);
}
/*package*/ void addAggregation(LinkDescriptor d) {
if (myAggregations == null) {
myAggregations = new ArrayList<>(4);
}
myAggregations.add(d);
}
/*package*/ SLanguageId languageId(long langIdHigh, long langIdLow) {
if (myLanguageHighUUID == langIdHigh && myLanguageLowUUID == langIdLow) {
return myConceptId.getLanguageId();
}
return MetaIdFactory.langId(langIdHigh, langIdLow);
}
/*package*/ static class LinkBuilder {
/*package*/ ConceptDescriptorBuilder2 myBuilder;
/*package*/ final String myLinkName;
/*package*/ final long myLinkId;
/*package*/ boolean myIsOptional;
/*package*/ SNodeReference myOrigin;
/*package*/ SConceptId myTargetConcept;
/*package*/ LinkBuilder(ConceptDescriptorBuilder2 builder, String linkName, long linkId) {
myBuilder = builder;
myLinkName = linkName;
myLinkId = linkId;
}
}
public static final class AssociationLinkBuilder extends LinkBuilder {
/*package*/ AssociationLinkBuilder(ConceptDescriptorBuilder2 builder, String name, long linkId) {
super(builder, name, linkId);
}
public ConceptDescriptorBuilder2 done() {
myBuilder.addAssociation(new BaseReferenceDescriptor(MetaIdFactory.refId(myBuilder.myConceptId, myLinkId), myLinkName, myTargetConcept, myIsOptional, myOrigin));
return myBuilder;
}
public AssociationLinkBuilder target(long langIdHigh, long langIdLow, long conceptId) {
myTargetConcept = MetaIdFactory.conceptId(myBuilder.languageId(langIdHigh, langIdLow), conceptId);
return this;
}
public AssociationLinkBuilder optional(boolean isOptional) {
myIsOptional = isOptional;
return this;
}
public AssociationLinkBuilder origin(SNodeReference srcNode) {
myOrigin = srcNode;
return this;
}
/**
* See {@link ConceptDescriptorBuilder2#prop(String, long, String)} for explanation of sourceNodeId:String
*/
public AssociationLinkBuilder origin(String srcNodeId) {
if (myBuilder.myOrigin != null && srcNodeId != null) {
myOrigin = new SNodePointer(myBuilder.myOrigin.getModelReference(), PersistenceFacade.getInstance().createNodeId(srcNodeId));
}
return this;
}
}
public static final class AggregationLinkBuilder extends LinkBuilder {
private boolean myIsMultiple = false;
private boolean myIsOrdered = true;
/*package*/ AggregationLinkBuilder(ConceptDescriptorBuilder2 builder, String linkName, long linkId) {
super(builder, linkName, linkId);
}
public ConceptDescriptorBuilder2 done() {
myBuilder.addAggregation(new BaseLinkDescriptor(MetaIdFactory.linkId(myBuilder.myConceptId, myLinkId), myLinkName, myTargetConcept, myIsOptional, myIsMultiple, !myIsOrdered, myOrigin));
return myBuilder;
}
public AggregationLinkBuilder target(long langIdHigh, long langIdLow, long conceptId) {
myTargetConcept = MetaIdFactory.conceptId(myBuilder.languageId(langIdHigh, langIdLow), conceptId);
return this;
}
public AggregationLinkBuilder optional(boolean isOptional) {
myIsOptional = isOptional;
return this;
}
public AggregationLinkBuilder origin(SNodeReference srcNode) {
myOrigin = srcNode;
return this;
}
/**
* See {@link ConceptDescriptorBuilder2#prop(String, long, String)} for explanation of sourceNodeId:String
*/
public AggregationLinkBuilder origin(String srcNodeId) {
if (myBuilder.myOrigin != null && srcNodeId != null) {
myOrigin = new SNodePointer(myBuilder.myOrigin.getModelReference(), PersistenceFacade.getInstance().createNodeId(srcNodeId));
}
return this;
}
public AggregationLinkBuilder multiple(boolean isMultiple) {
myIsMultiple = isMultiple;
return this;
}
public AggregationLinkBuilder ordered(boolean isOrdered) {
myIsOrdered = isOrdered;
return this;
}
}
}