/* * 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.core.aspects.behaviour; import jetbrains.mps.core.aspects.behaviour.api.SMethod; import jetbrains.mps.core.aspects.behaviour.api.SMethodId; import jetbrains.mps.smodel.JavaFriendlyBase64; import jetbrains.mps.smodel.SNodeId.Regular; import jetbrains.mps.util.EqualUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.annotations.Immutable; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.model.SNodeId; import org.jetbrains.mps.openapi.persistence.PersistenceFacade; /** * Represents a handle which uniquely identifies {@link SMethod} within the concept (including all the ancestors). * This implementation is based on the NodeId of the method node (in the behavior aspect of the concept) */ @Immutable public final class SMethodTrimmedId implements SMethodId { private final SAbstractConcept myConcept; private final SNodeId myNodeId; /** * @param concept is null iff is is a virtual method id (for them the concept does not belong to the id) */ private SMethodTrimmedId(@Nullable SAbstractConcept concept, @NotNull SNodeId id) { myConcept = concept; myNodeId = id; } @Override public int hashCode() { int conceptHash = myConcept != null ? myConcept.hashCode() : 0; return myNodeId.hashCode() + 31 * conceptHash; } @Override public boolean equals(Object o) { if (o instanceof SMethodTrimmedId) { return ((SMethodTrimmedId) o).myNodeId.equals(myNodeId) && EqualUtil.equals(((SMethodTrimmedId) o).myConcept, myConcept); } return false; } @Override public String toString() { return "SMethodId(" + myNodeId + ":" + myConcept + ")"; } @NotNull public static SMethodTrimmedId create(@NotNull String name, @Nullable SAbstractConcept concept, @NotNull String trimmedId) { SNodeId nodeId = fromText(trimmedId); return new SMethodTrimmedId(concept, nodeId); } /** * @param nodeId identity of the method declaration node * @return serializable value that identifies the method */ public static String toText(@NotNull SNodeId nodeId) { if (nodeId instanceof jetbrains.mps.smodel.SNodeId.Regular) { return new JavaFriendlyBase64().toString(((Regular) nodeId).getId()); } // XXX this is not a nice fallback as toString() gives value prefixed // with id kind. Generally we shall never get here, provided behavior models // are backed with regular SNodeId. However, if we imagine behavior model // with custom identity, I don't feel there's an easy way to deal with 'kind:' // mechanism. We need one to tell different SNodeId implementations to parse // value back, and escaping comes to mind (with dark magic not to contradict with // node's own serialization format), or a dedicated method to give value ready for // injecting into method name, with 'kind:' encoded according to SNodeId's // implementation (with extra API methods in PersistenceFacade) return nodeId.toString(); } /** * @param serializedNodeId value created by {@link #toText(SNodeId)} */ public static SNodeId fromText(@NotNull String serializedNodeId) { try { return new Regular(new JavaFriendlyBase64().parseLong(serializedNodeId)); } catch (IllegalArgumentException ex) { return PersistenceFacade.getInstance().createNodeId(serializedNodeId); // if there's another IAE, I can't do anything, just let RTE go up. } } public static SMethodTrimmedId create(@NotNull SNodeId nodeId, @Nullable SAbstractConcept concept) { return new SMethodTrimmedId(concept, nodeId); } }