/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * 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 com.android.dx.rop.code;

import com.android.dx.util.Hex;

/**
 * Constants used as "access flags" in various places in classes, and
 * related utilities. Although, at the rop layer, flags are generally
 * ignored, this is the layer of communication, and as such, this
 * package is where these definitions belong. The flag definitions are
 * identical to Java access flags, but <code>ACC_SUPER</code> isn't
 * used at all in translated code, and <code>ACC_SYNCHRONIZED</code>
 * is only used in a very limited way.
 */
public final class AccessFlags {
    /** public member / class */
    public static final int ACC_PUBLIC = 0x0001;

    /** private member */
    public static final int ACC_PRIVATE = 0x0002;

    /** protected member */
    public static final int ACC_PROTECTED = 0x0004;

    /** static member */
    public static final int ACC_STATIC = 0x0008;

    /** final member / class */
    public static final int ACC_FINAL = 0x0010;

    /**
     * synchronized method; only valid in dex files for <code>native</code>
     * methods
     */
    public static final int ACC_SYNCHRONIZED = 0x0020;

    /**
     * class with new-style <code>invokespecial</code> for superclass
     * method access 
     */
    public static final int ACC_SUPER = 0x0020;

    /** volatile field */
    public static final int ACC_VOLATILE = 0x0040;

    /** bridge method (generated) */
    public static final int ACC_BRIDGE = 0x0040;

    /** transient field */
    public static final int ACC_TRANSIENT = 0x0080;

    /** varargs method */
    public static final int ACC_VARARGS = 0x0080;

    /** native method */
    public static final int ACC_NATIVE = 0x0100;

    /** "class" is in fact an public static final interface */
    public static final int ACC_INTERFACE = 0x0200;

    /** abstract method / class */
    public static final int ACC_ABSTRACT = 0x0400;

    /**
     * method with strict floating point (<code>strictfp</code>)
     * behavior 
     */
    public static final int ACC_STRICT = 0x0800;

    /** synthetic member */
    public static final int ACC_SYNTHETIC = 0x1000;

    /** class is an annotation type */
    public static final int ACC_ANNOTATION = 0x2000;

    /**
     * class is an enumerated type; field is an element of an enumerated
     * type 
     */
    public static final int ACC_ENUM = 0x4000;

    /** method is a constructor */
    public static final int ACC_CONSTRUCTOR = 0x10000;

    /**
     * method was declared <code>synchronized</code>; has no effect on
     * execution (other than inspecting this flag, per se)
     */
    public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;

    /** flags defined on classes */
    public static final int CLASS_FLAGS =
        ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT |
        ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM;

    /** flags defined on inner classes */
    public static final int INNER_CLASS_FLAGS =
        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
        ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION |
        ACC_ENUM;

    /** flags defined on fields */
    public static final int FIELD_FLAGS =
        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
        ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM;

    /** flags defined on methods */
    public static final int METHOD_FLAGS =
        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
        ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE |
        ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR |
        ACC_DECLARED_SYNCHRONIZED;

    /** indicates conversion of class flags */
    private static final int CONV_CLASS = 1;

    /** indicates conversion of field flags */
    private static final int CONV_FIELD = 2;

    /** indicates conversion of method flags */
    private static final int CONV_METHOD = 3;

    /**
     * This class is uninstantiable.
     */
    private AccessFlags() {
        // This space intentionally left blank.
    }

    /**
     * Returns a human-oriented string representing the given access flags,
     * as defined on classes (not fields or methods).
     * 
     * @param flags the flags
     * @return non-null; human-oriented string
     */
    public static String classString(int flags) {
        return humanHelper(flags, CLASS_FLAGS, CONV_CLASS);
    }

    /**
     * Returns a human-oriented string representing the given access flags,
     * as defined on inner classes.
     * 
     * @param flags the flags
     * @return non-null; human-oriented string
     */
    public static String innerClassString(int flags) {
        return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS);
    }

    /**
     * Returns a human-oriented string representing the given access flags,
     * as defined on fields (not classes or methods).
     * 
     * @param flags the flags
     * @return non-null; human-oriented string
     */
    public static String fieldString(int flags) {
        return humanHelper(flags, FIELD_FLAGS, CONV_FIELD);
    }

    /**
     * Returns a human-oriented string representing the given access flags,
     * as defined on methods (not classes or fields).
     * 
     * @param flags the flags
     * @return non-null; human-oriented string
     */
    public static String methodString(int flags) {
        return humanHelper(flags, METHOD_FLAGS, CONV_METHOD);
    }

    /**
     * Returns whether the flag <code>ACC_PUBLIC</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_PUBLIC</code> flag
     */
    public static boolean isPublic(int flags) {
        return (flags & ACC_PUBLIC) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_PROTECTED</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_PROTECTED</code> flag
     */
    public static boolean isProtected(int flags) {
        return (flags & ACC_PROTECTED) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_PRIVATE</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_PRIVATE</code> flag
     */
    public static boolean isPrivate(int flags) {
        return (flags & ACC_PRIVATE) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_STATIC</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_STATIC</code> flag
     */
    public static boolean isStatic(int flags) {
        return (flags & ACC_STATIC) != 0;
    }
    
    /**
     * Returns whether the flag <code>ACC_SYNCHRONIZED</code> is on in
     * the given flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_SYNCHRONIZED</code> flag
     */
    public static boolean isSynchronized(int flags) {
        return (flags & ACC_SYNCHRONIZED) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_ABSTRACT</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_ABSTRACT</code> flag
     */
    public static boolean isAbstract(int flags) {
        return (flags & ACC_ABSTRACT) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_NATIVE</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_NATIVE</code> flag
     */
    public static boolean isNative(int flags) {
        return (flags & ACC_NATIVE) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_ANNOTATION</code> is on in the given
     * flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_ANNOTATION</code> flag
     */
    public static boolean isAnnotation(int flags) {
        return (flags & ACC_ANNOTATION) != 0;
    }

    /**
     * Returns whether the flag <code>ACC_DECLARED_SYNCHRONIZED</code> is
     * on in the given flags.
     * 
     * @param flags the flags to check
     * @return the value of the <code>ACC_DECLARED_SYNCHRONIZED</code> flag
     */
    public static boolean isDeclaredSynchronized(int flags) {
        return (flags & ACC_DECLARED_SYNCHRONIZED) != 0;
    }

    /**
     * Helper to return a human-oriented string representing the given
     * access flags.
     * 
     * @param flags the defined flags
     * @param mask mask for the "defined" bits
     * @param what what the flags represent (one of <code>CONV_*</code>)
     * @return non-null; human-oriented string
     */
    private static String humanHelper(int flags, int mask, int what) {
        StringBuffer sb = new StringBuffer(80);
        int extra = flags & ~mask;

        flags &= mask;

        if ((flags & ACC_PUBLIC) != 0) {
            sb.append("|public");
        }
        if ((flags & ACC_PRIVATE) != 0) {
            sb.append("|private");
        }
        if ((flags & ACC_PROTECTED) != 0) {
            sb.append("|protected");
        }
        if ((flags & ACC_STATIC) != 0) {
            sb.append("|static");
        }
        if ((flags & ACC_FINAL) != 0) {
            sb.append("|final");
        }
        if ((flags & ACC_SYNCHRONIZED) != 0) {
            if (what == CONV_CLASS) {
                sb.append("|super");
            } else {
                sb.append("|synchronized");
            }
        }
        if ((flags & ACC_VOLATILE) != 0) {
            if (what == CONV_METHOD) {
                sb.append("|bridge");
            } else {
                sb.append("|volatile");
            }
        }
        if ((flags & ACC_TRANSIENT) != 0) {
            if (what == CONV_METHOD) {
                sb.append("|varargs");
            } else {
                sb.append("|transient");
            }
        }
        if ((flags & ACC_NATIVE) != 0) {
            sb.append("|native");
        }
        if ((flags & ACC_INTERFACE) != 0) {
            sb.append("|interface");
        }
        if ((flags & ACC_ABSTRACT) != 0) {
            sb.append("|abstract");
        }
        if ((flags & ACC_STRICT) != 0) {
            sb.append("|strictfp");
        }
        if ((flags & ACC_SYNTHETIC) != 0) {
            sb.append("|synthetic");
        }
        if ((flags & ACC_ANNOTATION) != 0) {
            sb.append("|annotation");
        }
        if ((flags & ACC_ENUM) != 0) {
            sb.append("|enum");
        }
        if ((flags & ACC_CONSTRUCTOR) != 0) {
            sb.append("|constructor");
        }
        if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
            sb.append("|declared_synchronized");
        }

        if ((extra != 0) || (sb.length() == 0)) {
            sb.append('|');
            sb.append(Hex.u2(extra));
        }

        return sb.substring(1);
    }
}
