001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.weaver.privilizer;
020
021import java.lang.reflect.Modifier;
022import java.util.EnumSet;
023import java.util.Locale;
024import java.util.Set;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.commons.lang3.Validate;
028
029/**
030 * Enumerates the Java access levels.
031 */
032public enum AccessLevel {
033    /**
034     * {@code public}.
035     */
036    PUBLIC(Modifier.PUBLIC),
037
038    /**
039     * {@code protected}.
040     */
041    PROTECTED(Modifier.PROTECTED),
042
043    /**
044     * {@code ""}.
045     */
046    PACKAGE(0),
047
048    /**
049     * {@code private}.
050     */
051    PRIVATE(Modifier.PRIVATE);
052
053    private final int flag;
054
055    private AccessLevel(final int flag) {
056        this.flag = flag;
057    }
058
059    /**
060     * Get the {@link AccessLevel} specified by a Java modifier.
061     * @param mod from which to extract
062     * @return {@link AccessLevel}
063     * @throws IllegalArgumentException if multiple access modifiers specified
064     */
065    public static AccessLevel of(final int mod) {
066        final Set<AccessLevel> matched = EnumSet.noneOf(AccessLevel.class);
067        if (Modifier.isPublic(mod)) {
068            matched.add(PUBLIC);
069        }
070        if (Modifier.isProtected(mod)) {
071            matched.add(PROTECTED);
072        }
073        if (Modifier.isPrivate(mod)) {
074            matched.add(PRIVATE);
075        }
076        if (matched.isEmpty()) {
077            return PACKAGE;
078        }
079        Validate.isTrue(matched.size() == 1, "%s seems to declare multiple access modifiers: %s", mod, matched);
080        return matched.iterator().next();
081    }
082
083    /**
084     * Overlay this {@link AccessLevel} onto a Java modifier value.
085     * @param mod input
086     * @return {@code mod}, with this {@link AccessLevel}
087     */
088    public int merge(final int mod) {
089        int remove = 0;
090        for (final AccessLevel accessLevel : EnumSet.complementOf(EnumSet.of(this))) {
091            remove |= accessLevel.flag;
092        }
093        return mod & ~remove | flag;
094    }
095
096    /**
097     * Render this {@link AccessLevel} as a {@link String}.
098     * @return {@link String}
099     */
100    @Override
101    public String toString() {
102        return name().toLowerCase(Locale.US);
103    }
104
105    /**
106     * Get the {@link AccessLevel} value that should be used as a default.
107     * This is <em>not</em> "default"/{@code package} access, but rather the default
108     * value that should be used for privilizer weaving.
109     * @return {@link AccessLevel#PRIVATE}
110     */
111    public static AccessLevel defaultValue() {
112        return AccessLevel.PRIVATE;
113    }
114
115    /**
116     * Parse from a {@link String} returning {@link #defaultValue()} for blank/null input.
117     * @param str to parse
118     * @return {@link AccessLevel}
119     */
120    public static AccessLevel parse(final String str) {
121        if (StringUtils.isBlank(str)) {
122            return defaultValue();
123        }
124        return valueOf(str.trim().toUpperCase(Locale.US));
125    }
126}