blob: 7ee6949b8f60e711f342f1799ddb128e3fa0f16a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.openjpa.meta;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.UserException;
/**
* Represents access styles for members of a class and field through a
* 5-bit integer.
* <br>
*
* The bits designate following aspects of access style being used at class
* level:<br>
*
* <LI>Bit position 0 (UNKNOWN): generally 0. All bits as zero represent
* that no access style has been set. 1 denotes that the
* class has no property at all and its access can not be
* determined.
* <LI>Bit position 1 (FIELD): Field based access is default
* <LI>Bit position 2 (PROPERTY): Property based access is default
* <LI>Bit position 3 (EXPLICIT): whether explicit or implicit
* Explicit access style allows members to use mixed access style,
* implicit access style does not
* <LI>Bit position 4 (MIXED): whether all members are using the same
* access style or not. Can only be set if EXPLICT bit is set.
* If set, then bit 1 or 2 denotes what is the default.
* <br>
* The same bits designate following aspects of access style being used at field
* level:<br>
*
* <LI>Bit position 0 (UNKNOWN): always 0. All bits as zero represent
* that no access style has been set.
* <LI>Bit position 1 (FIELD): Field based access is default
* <LI>Bit position 2 (PROPERTY): Property based access is default
* <LI>Bit position 3 (EXPLICIT): whether the access is explicit or implicit
* <LI>Bit position 4 (MIXED): not used
* <br>
*
* <p>
* Validation Rules for the bits:<br>
* <LI>1. Only one of the position 1 (FIELD) and 2 (PROPERTY) can
* be set. A single bit is not used for legacy reason to cope with the access
* constants used in ClassMetaData which this class now refactors to address
* new behaviors specified in JPA 2.0 specification.
* <LI>2. if position 3 (EXPLICIT) is set then one of position 1
* (FIELD) and 2 (PROPERTY) must be set.
* <LI>3. If position 4 (MIXED) is set then the set position of either
* FIELD or PROPERTY designates the default access of the
* member.
*
* @author Pinaki Poddar
*
* @since 2.0.0
*/
public class AccessCode {
public static int UNKNOWN = 0;
public static int EMPTY = 1;
public static int FIELD = 2 << 0;
public static int PROPERTY = 2 << 1;
public static int EXPLICIT = 2 << 2;
public static int MIXED = 2 << 3;
private static Localizer _loc = Localizer.forPackage(AccessCode.class);
/**
* Affirms if the given code is valid.
*/
public static boolean isValidClassCode(int code) {
if (code == EMPTY)
return true;
return (code%2 == 0 || code == EMPTY)
&& code >= UNKNOWN
&& code <= (MIXED|EXPLICIT|PROPERTY)
&& !(isProperty(code) && isField(code)) // both 1 & 2 can not be set
&& (isProperty(code) || isField(code) || isUnknown(code))
&& ((isMixed(code) && isExplicit(code)) || !isMixed(code));
}
public static boolean isValidFieldCode(int code) {
return code%2 == 0 // code must be even
&& code >= UNKNOWN
&& code <= (EXPLICIT|PROPERTY)
&& !(isProperty(code) && isField(code))
&& (isProperty(code) || isField(code) || isUnknown(code));
}
/**
* Affirms if the given code designates that members can use both
* FIELD and PROPERTY based access.
*/
public static boolean isMixed(int code) {
return (code & MIXED) != 0;
}
public static boolean isExplicit(int code) {
return (code & EXPLICIT) != 0;
}
public static boolean isProperty(int code) {
return (code & PROPERTY) != 0;
}
public static boolean isField(int code) {
return (code & FIELD) != 0;
}
public static boolean isUnknown(int code) {
return code == UNKNOWN;
}
public static boolean isEmpty(int code) {
return code == EMPTY;
}
public static boolean isField(ClassMetaData meta) {
return isField(meta.getAccessType());
}
public static boolean isProperty(ClassMetaData meta) {
return isProperty(meta.getAccessType());
}
public static boolean isUnknown(ClassMetaData meta) {
return isUnknown(meta.getAccessType());
}
public static boolean isEmpty(ClassMetaData meta) {
return isEmpty(meta.getAccessType());
}
public static boolean isField(FieldMetaData meta) {
return isField(meta.getAccessType());
}
public static boolean isProperty(FieldMetaData meta) {
return isProperty(meta.getAccessType());
}
public static boolean isUnknown(FieldMetaData meta) {
return isUnknown(meta.getAccessType());
}
/**
* Affirms if the sub class access type is compatible with super class
* access style.
*/
public static boolean isCompatibleSuper(int subCode, int superCode) {
if (isEmpty(superCode))
return true;
if (isValidClassCode(subCode) && isValidClassCode(superCode)) {
if (isExplicit(subCode))
return true;
return subCode == superCode;
}
return false;
}
public static int mergeFieldCode(ClassMetaData meta, FieldMetaData fmd,
int fCode) {
int cCode = meta.getAccessType();
try {
return mergeFieldCode(cCode, fCode);
} catch (IllegalStateException e) {
throw new UserException(_loc.get("access-illegal-merge",
fmd.getFullName(false), toFieldString(fCode),
toClassString(cCode)));
}
}
/**
* Merges the field access type with the class access type provided such
* merge is valid.
*
* @return the modified class access code.
* @exception IllegalStateException if the given codes are not compatible
*/
public static int mergeFieldCode(int cCode, int fCode) {
if (isValidClassCode(cCode) && isValidFieldCode(fCode)) {
if (isUnknown(cCode)) {
if (isUnknown(fCode))
return UNKNOWN;
return isProperty(fCode) ? PROPERTY : FIELD;
}
boolean mixed = isProperty(cCode) != isProperty(fCode);
if (isExplicit(cCode)) {
if (mixed) {
return MIXED | cCode;
} else {
return cCode;
}
} else { // default, implicit access
if (fCode == cCode)
return cCode;
else
throw new IllegalStateException(
(_loc.get("access-cannot-merge",
toFieldString(fCode),
toClassString(cCode)).toString()));
}
}
return cCode;
}
public static int getMixedCode(int cCode, int fCode) {
if (isMixed(cCode) || (isProperty(cCode) == isProperty(fCode)))
return cCode;
return MIXED | cCode;
}
public static int toFieldCode(int code) {
if (isProperty(code))
return PROPERTY;
if (isField(code))
return FIELD;
return UNKNOWN;
}
public static String toFieldString(int code) {
if (!isValidFieldCode(code))
return "invalid code " + code;
if (code == UNKNOWN)
return "unknown access";
if (code == EMPTY)
return "empty access";
return (isMixed(code) ? "mixed " : "")
+ (isExplicit(code) ? "explicit " : "implicit ")
+ (isField(code) ? "field" : "property")
+ " access";
}
public static String toClassString(int code) {
if (!isValidClassCode(code))
return "invalid code " + code;
if (code == UNKNOWN)
return "unknown access";
if (code == EMPTY)
return "empty access";
return (isMixed(code) ? "mixed " : "")
+ (isExplicit(code) ? "explicit " : "implicit ")
+ (isField(code) ? "field" : "property")
+ " access";
}
}