/* * 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.java.stub; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.persistence.Memento; import java.util.ArrayList; import java.util.List; /** * Manage access to a subset of packages available from a source (e.g. model root) and classes in these packages. * Comes handy when we want to control set of packages coming from a well-known classpath item, e.g. Java SDK * classes, when we want to expose only specific, public APIs. * * @author Artem Tikhomirov */ public final class PackageScopeControl { private boolean mySkipPrivate = false; private List<String> myIncludePrefix; private List<String> myExcludePrefix; public PackageScopeControl() { } // caller controls element in memento we write to, e.g. if it needs to write few scopes, it's up to caller to // name them and to place in a container public void save(Memento memento) { memento.put("skip-private", Boolean.toString(mySkipPrivate)); if (myIncludePrefix != null) { for (String prefix : myIncludePrefix) { memento.createChild("include").put("prefix", prefix); } } if (myExcludePrefix != null) { for (String prefix : myExcludePrefix) { memento.createChild("exclude").put("prefix", prefix); } } } public void load(Memento memento) { mySkipPrivate = Boolean.parseBoolean(memento.get("skip-private")); for (Memento entry : memento.getChildren("include")) { includeWithPrefix(entry.get("prefix")); } for (Memento entry : memento.getChildren("exclude")) { excludeWithPrefix(entry.get("prefix")); } } /** * Package name prefix. To match exact package, not any starting with the prefix, don't forget * to put dot in the end of the prefix (e.g. "org.com." not to match "org.common") * * @param packageNamePrefix qualified package name prefix to match */ public void includeWithPrefix(@NotNull String packageNamePrefix) { if (myIncludePrefix == null) { myIncludePrefix = new ArrayList<String>(4); } if (packageNamePrefix.length() == 0) { throw new IllegalArgumentException(); } myIncludePrefix.add(packageNamePrefix); } public void excludeWithPrefix(@NotNull String packageNamePrefix) { if (myExcludePrefix == null) { myExcludePrefix = new ArrayList<String>(4); } if (packageNamePrefix.length() == 0) { throw new IllegalArgumentException(); } myExcludePrefix.add(packageNamePrefix); } public boolean isSkipPrivate() { return mySkipPrivate; } public void setSkipPrivate(boolean skipPrivate) { mySkipPrivate = skipPrivate; } /** * First, package name is checked for inclusion, then for exclusion (so that * <code>include("com"); exclude("com.b.")</code> would allow <code>com.a.A</code>, <code>com.D</code> * and forbid <code>com.b.C</code>. * * @param qualifiedPackageName package to check for scope * @return true if package deemed part of the scope */ public boolean isIncluded(@NotNull String qualifiedPackageName) { boolean included = myIncludePrefix == null || matchPrefix(myIncludePrefix, qualifiedPackageName, false); boolean excluded = myExcludePrefix != null && matchPrefix(myExcludePrefix, qualifiedPackageName, false); return included && !excluded; } /** * Checking if any of sub-packages of this package is included by {@link #myIncludePrefix} * * @param qualifiedPackageName package to check for scope * @return true if at least one of child packages may be included */ public boolean isAnyChildIncluded(@NotNull String qualifiedPackageName) { return myIncludePrefix != null && matchPrefix(myIncludePrefix, qualifiedPackageName, true); } /** * @param prefixes - the list of prefixes * @param name - the name of the package * @param checkingChildPrefixes - true the name is a name of potential parent package for any of included packages (prefixes), * used from {@link #isAnyChildIncluded(String)} * @return boolean if package name starts with any of specified prefixes of vice versa (if <code>checkingChildPrefixes</code> is true) */ private static boolean matchPrefix(List<String> prefixes, String name, boolean checkingChildPrefixes) { // prefix may indicate exact package, thus we make sure name looks 'complete' // e.g. prefix "org.com." shall match name == "org.com" name = name + '.'; for (String prefix : prefixes) { if (checkingChildPrefixes ? prefix.startsWith(name) : name.startsWith(prefix)) { return true; } } return false; } }