/* * 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; } } }