/* * 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.extapi.persistence; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.annotations.Immutable; import java.text.MessageFormat; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; /** * Maps {@link SourceRootKind} to {@link SourceRoot}. * Preserves the order of the <code>SourcePath</code>s. */ final class SourcePaths { private final Map<SourceRootKind, List<SourceRoot>> myFileKind2SourcePaths = new LinkedHashMap<>(); @NotNull private final Predicate<SourceRootKind> isFileKindAllowed; public SourcePaths() { this((kind) -> true); // always allows } public SourcePaths(@NotNull Predicate<SourceRootKind> isFileKindAllowed0) { isFileKindAllowed = isFileKindAllowed0; } /** * @return an empty list if the kind is not registered. */ @NotNull @Immutable public synchronized List<SourceRoot> getByKind(@NotNull SourceRootKind kind) { List<SourceRoot> list = myFileKind2SourcePaths.get(kind); return list != null ? unmodifiableList(list) : emptyList(); } /** * Adds source roots to the container * * @throws FileKindIsNotAllowedException if provided file kind is not allowed * @throws SourceRootAlreadyExistsException if such source path already exists */ public synchronized void addSourceRoot(@NotNull SourceRootKind kind, @NotNull SourceRoot root) { if (!isKindAllowed(kind)) { throw new FileKindIsNotAllowedException(kind, root); } SourceRootKind existingRootKind = getKind(root); if (existingRootKind != null) { throw new SourceRootAlreadyExistsException(root, kind, root, existingRootKind); } myFileKind2SourcePaths.putIfAbsent(kind, new ArrayList<>()); myFileKind2SourcePaths.get(kind).add(root); } private boolean isKindAllowed(@NotNull SourceRootKind kind) { return isFileKindAllowed.test(kind); } @Nullable private SourceRootKind getKind(@NotNull SourceRoot root) { for (SourceRootKind kind : myFileKind2SourcePaths.keySet()) { List<SourceRoot> sourceRoots = myFileKind2SourcePaths.get(kind); if (sourceRoots.contains(root)) { return kind; } } return null; } @Nullable public SourceRootKind removeSourceRoot(SourceRoot root) { for (SourceRootKind kind : myFileKind2SourcePaths.keySet()) { List<SourceRoot> sourceRoots = myFileKind2SourcePaths.get(kind); if (sourceRoots.contains(root)) { sourceRoots.remove(root); return kind; } } return null; } public void clearAll() { myFileKind2SourcePaths.clear(); } @Override public int hashCode() { return Objects.hash(myFileKind2SourcePaths); } @Override public boolean equals(Object obj) { if (obj instanceof SourcePaths) { return Objects.equals(myFileKind2SourcePaths, ((SourcePaths) obj).myFileKind2SourcePaths); } return false; } @Override public String toString() { return "SourcePaths [" + myFileKind2SourcePaths.values().size() + " source root paths]"; } public static final class SourceRootAlreadyExistsException extends IllegalStateException { private final SourceRoot myRoot; private final SourceRootKind myPathKind; private final SourceRoot myExistingRoot; private final SourceRootKind myExistingRootKind; public SourceRootAlreadyExistsException(SourceRoot root, SourceRootKind pathKind, SourceRoot existingRoot, SourceRootKind existingPathKind) { myRoot = root; myPathKind = pathKind; myExistingRoot = existingRoot; myExistingRootKind = existingPathKind; } @Override @NotNull public String getMessage() { return MessageFormat.format("Trying to register the source root: [{1}, kind {2}] whilst [{3}, kind {4}] " + "is already registered", myRoot, myPathKind, myExistingRoot, myExistingRootKind); } } }