blob: 9086422f0fbe6677af48ec74524909097543084a [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.MetaDataException;
import org.apache.openjpa.util.UserException;
/**
* Default {@link ValueMetaData} implementation.
*
* @author Abe White
*/
public class ValueMetaDataImpl
implements ValueMetaData {
private static final long serialVersionUID = 6766697443293395831L;
private static final Localizer _loc = Localizer.forPackage
(ValueMetaDataImpl.class);
///////////////////////////////////////////////////////////////
// Note: if you add additional state that should be copied to
// embedded metadata, make sure to add it to the copy() method
///////////////////////////////////////////////////////////////
private FieldMetaData _owner;
private Class _decType = Object.class;
private int _decCode = JavaTypes.OBJECT;
private ClassMetaData _decTypeMeta = null;
private Class _type = null;
private int _code = JavaTypes.OBJECT;
private ClassMetaData _typeMeta = null;
private Class _typeOverride = null;
private int _delete = CASCADE_NONE;
private int _persist = CASCADE_AUTO;
private int _attach = CASCADE_IMMEDIATE;
private int _detach = CASCADE_AUTO;
private int _refresh = CASCADE_AUTO;
private boolean _serialized = false;
private Boolean _embedded = null;
private ClassMetaData _embeddedMeta = null;
private int _resMode = MODE_NONE;
private String _mappedBy = null;
private FieldMetaData _mappedByMeta = null;
private boolean _checkPUDefaultCascadePersist = true;
protected ValueMetaDataImpl(FieldMetaData owner) {
_owner = owner;
}
/**
* Constructor for serialization.
*/
protected ValueMetaDataImpl() {
}
@Override
public FieldMetaData getFieldMetaData() {
return _owner;
}
@Override
public MetaDataRepository getRepository() {
return _owner.getRepository();
}
@Override
public Class getType() {
return (_type == null) ? _decType : _type;
}
@Override
public void setType(Class type) {
_type = type;
_typeMeta = null;
if (type != null)
setTypeCode(JavaTypes.getTypeCode(type));
}
@Override
public int getTypeCode() {
return (_type == null) ? _decCode : _code;
}
@Override
public void setTypeCode(int code) {
_code = code;
}
@Override
public boolean isTypePC() {
return getTypeCode() == JavaTypes.PC
|| getTypeCode() == JavaTypes.PC_UNTYPED;
}
@Override
public ClassMetaData getTypeMetaData() {
if (_type == null)
return getDeclaredTypeMetaData();
if (_typeMeta == null && _code == JavaTypes.PC) {
ClassMetaData meta = _owner.getDefiningMetaData();
_typeMeta = meta.getRepository().getMetaData(_type,
meta.getEnvClassLoader(), true);
}
return _typeMeta;
}
@Override
public Class getDeclaredType() {
return _decType;
}
@Override
public void setDeclaredType(Class type) {
_decType = type;
_decTypeMeta = null;
_decCode = JavaTypes.getTypeCode(type);
if (_embeddedMeta != null)
_embeddedMeta.setDescribedType(type);
}
@Override
public int getDeclaredTypeCode() {
return _decCode;
}
@Override
public void setDeclaredTypeCode(int code) {
_decCode = code;
}
@Override
public boolean isDeclaredTypePC() {
return _decCode == JavaTypes.PC || _decCode == JavaTypes.PC_UNTYPED;
}
@Override
public ClassMetaData getDeclaredTypeMetaData() {
if (_decTypeMeta == null && _decCode == JavaTypes.PC) {
if (isEmbedded())
_decTypeMeta = getEmbeddedMetaData();
else {
ClassMetaData meta = _owner.getDefiningMetaData();
_decTypeMeta = meta.getRepository().getMetaData(_decType,
meta.getEnvClassLoader(), true);
}
}
return _decTypeMeta;
}
@Override
public boolean isEmbedded() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
return false;
if (_embedded == null) {
// field left as default; embedded setting depends on type
switch (_decCode) {
case JavaTypes.PC:
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
case JavaTypes.PC_UNTYPED:
_embedded = Boolean.FALSE;
break;
default:
_embedded = Boolean.TRUE;
}
}
return _embedded;
}
@Override
public void setEmbedded(boolean embedded) {
if (embedded && _embedded != Boolean.TRUE) {
_decTypeMeta = null;
_typeMeta = null;
}
_embedded = (embedded) ? Boolean.TRUE : Boolean.FALSE;
}
@Override
public boolean isEmbeddedPC() {
return _decCode == JavaTypes.PC && isEmbedded();
}
@Override
public ClassMetaData getEmbeddedMetaData() {
if (_embeddedMeta == null && isEmbeddedPC())
addEmbeddedMetaData();
return _embeddedMeta;
}
@Override
public ClassMetaData addEmbeddedMetaData(int access) {
MetaDataRepository repos = _owner.getRepository();
_embeddedMeta = repos.newEmbeddedClassMetaData(this);
_embeddedMeta.setDescribedType(_decType);
repos.getMetaDataFactory().getDefaults().populate(_embeddedMeta,
access);
setEmbedded(true);
return _embeddedMeta;
}
@Override
public ClassMetaData addEmbeddedMetaData() {
return addEmbeddedMetaData(AccessCode.UNKNOWN);
}
@Override
public int getCascadeDelete() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
return CASCADE_NONE;
if (isEmbeddedPC())
return CASCADE_IMMEDIATE;
switch (_delete) {
case CASCADE_NONE:
// if the user marks the owning field dependent and we
// externalize to a pc type, then become dependent
if (this != _owner.getValue() && isTypePC()
&& ((ValueMetaDataImpl) _owner.getValue())._delete
== CASCADE_AUTO)
return CASCADE_AUTO;
break;
case CASCADE_AUTO:
if (isTypePC())
return CASCADE_AUTO;
break;
case CASCADE_IMMEDIATE:
if (isDeclaredTypePC())
return CASCADE_IMMEDIATE;
break;
}
return CASCADE_NONE;
}
@Override
public void setCascadeDelete(int delete) {
_delete = delete;
}
@Override
public int getCascadePersist() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
return CASCADE_NONE;
if (isDeclaredTypePC())
return checkPUDefaultCascadePersist();
if (!isTypePC())
return CASCADE_NONE;
// if only externalized type is pc, can't cascade immediate
return (_persist == CASCADE_IMMEDIATE) ? CASCADE_AUTO : checkPUDefaultCascadePersist();
}
/**
* Check if the persistence unit default <cascade-persist> has been enabled. If so, then change
* CASCADE_NONE to CASCADE_IMMEDIATE.
*/
private int checkPUDefaultCascadePersist() {
if (_checkPUDefaultCascadePersist) {
// Apply default <cascade-persist> only to entity relationships
boolean applyDefaultCascadePersist = false;
switch (_owner.getAssociationType()) {
case FieldMetaData.ONE_TO_ONE:
case FieldMetaData.ONE_TO_MANY:
case FieldMetaData.MANY_TO_MANY:
case FieldMetaData.MANY_TO_ONE:
applyDefaultCascadePersist = true;
default:
}
if (applyDefaultCascadePersist) {
Boolean dcpe = getRepository().getMetaDataFactory().getDefaults().isDefaultCascadePersistEnabled();
if (dcpe != null && dcpe.equals(Boolean.TRUE) && _persist == CASCADE_NONE) {
_persist = CASCADE_IMMEDIATE;
}
}
_checkPUDefaultCascadePersist = false;
}
return _persist;
}
@Override
public void setCascadePersist(int persist) {
setCascadePersist(persist, true);
}
@Override
public void setCascadePersist(int persist, boolean checkPUDefault) {
_persist = persist;
_checkPUDefaultCascadePersist = checkPUDefault;
}
@Override
public int getCascadeAttach() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT
|| !isDeclaredTypePC()) // attach acts on declared type
return CASCADE_NONE;
if (isEmbeddedPC())
return CASCADE_IMMEDIATE;
return _attach;
}
@Override
public void setCascadeAttach(int attach) {
if (attach == CASCADE_AUTO)
throw new IllegalArgumentException("CASCADE_AUTO");
_attach = attach;
}
@Override
public int getCascadeDetach() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT
|| !isDeclaredTypePC()) // detach acts on declared type
return CASCADE_NONE;
if (isEmbedded())
return CASCADE_IMMEDIATE;
return _detach;
}
@Override
public void setCascadeDetach(int detach) {
_detach = detach;
}
@Override
public int getCascadeRefresh() {
if (_owner.getManagement() != FieldMetaData.MANAGE_PERSISTENT
|| !isDeclaredTypePC()) // refresh acts on declared type
return CASCADE_NONE;
return _refresh;
}
@Override
public void setCascadeRefresh(int refresh) {
_refresh = refresh;
}
@Override
public boolean isSerialized() {
return _serialized;
}
@Override
public void setSerialized(boolean serialized) {
_serialized = serialized;
}
@Override
public String getValueMappedBy() {
if (_mappedBy == MAPPED_BY_PK) {
// use this instead of getting meta from element b/c that
// requires element to be resolved
ClassMetaData meta = getRepository().getMetaData
(_owner.getElement().getType(), null, false);
if (meta == null)
throw new MetaDataException(_loc.get("val-not-pc", _owner));
if (meta.getPrimaryKeyFields().length != 1)
throw new MetaDataException(_loc.get("val-not-one-pk",
_owner));
_mappedByMeta = meta.getPrimaryKeyFields()[0];
_mappedBy = _mappedByMeta.getName();
}
return _mappedBy;
}
@Override
public void setValueMappedBy(String mapped) {
if (_owner.getKey() != this && mapped != null)
throw new UserException(_loc.get("mapped-by-not-key", this));
else {
_mappedBy = mapped;
_mappedByMeta = null;
}
}
@Override
public FieldMetaData getValueMappedByMetaData() {
if (getValueMappedBy() != null && _mappedByMeta == null) {
ClassMetaData meta = _owner.getElement().getTypeMetaData();
FieldMetaData field = (meta == null) ? null
: meta.getField(getValueMappedBy());
if (field == null)
throw new MetaDataException(_loc.get("no-mapped-by", this,
getValueMappedBy()));
if (field.getMappedBy() != null)
throw new MetaDataException(_loc.get("circ-mapped-by", this,
getValueMappedBy()));
_mappedByMeta = field;
}
return _mappedByMeta;
}
@Override
public Class getTypeOverride() {
return _typeOverride;
}
@Override
public void setTypeOverride(Class val) {
_typeOverride = val;
}
@Override
public String toString() {
String ret = _owner.getFullName(true);
if (this == _owner.getKey())
return ret + "<key:" + _decType + ">";
if (this == _owner.getElement()) {
if (_owner.getTypeCode() == JavaTypes.MAP)
return ret + "<value:" + _decType + ">";
return ret + "<element:" + _decType + ">";
}
return ret + "<" + _decType + ">";
}
////////////////////////
// Resolve and validate
////////////////////////
@Override
public int getResolve() {
return _resMode;
}
@Override
public void setResolve(int mode) {
_resMode = mode;
}
@Override
public void setResolve(int mode, boolean on) {
if (mode == MODE_NONE)
_resMode = mode;
else if (on)
_resMode |= mode;
else
_resMode &= ~mode;
}
@Override
public boolean resolve(int mode) {
if ((_resMode & mode) == mode)
return true;
int cur = _resMode;
_resMode |= mode;
// we only perform actions for meta mode
if ((mode & MODE_META) == 0 || (cur & MODE_META) != 0)
return false;
// check for type extension
int codeOverride = JavaTypes.OBJECT;
if (_typeOverride != null) {
codeOverride = JavaTypes.getTypeCode(_typeOverride);
// if there is no externalizer method or this value is a key or
// element, set our type to the type extension; otherwise, use the
// type extension as a hint to the actual type of the declared
// value (e.g. marking an interface as non-pc)
if (_owner.getExternalizerMethod() == null
|| _owner.getValue() != this) {
_type = _typeOverride;
_code = codeOverride;
} else {
_decCode = codeOverride;
if (JavaTypes.maybePC(codeOverride, _typeOverride))
resolveDeclaredType(_typeOverride);
}
}
// see if actual type is pc
if (JavaTypes.maybePC(_code, _type)) {
_typeMeta = _owner.getRepository().getMetaData(_type,
_owner.getDefiningMetaData().getEnvClassLoader(), false);
if (_typeMeta != null)
_code = JavaTypes.PC;
}
// if there is no externalizer, set our declared type code to the
// actual type so that we treat the value correctly at runtime
// (pers by reach, etc)
if (_typeOverride != null && _owner.getExternalizerMethod() == null
&& _owner.getExternalValues() == null) {
// cache the metadata immediately since we won't be able to get
// it lazily, since we're not resetting _decType to _type
_decCode = _code;
_decTypeMeta = _typeMeta;
} else if (JavaTypes.maybePC(_decCode, _decType))
resolveDeclaredType(_decType);
// resolves mapped by
getValueMappedBy();
ClassMetaData embed = getEmbeddedMetaData();
if (embed != null)
embed.resolve(MODE_META);
// oid as primary key field?
if (_decCode == JavaTypes.PC && isEmbedded()
&& _owner.isPrimaryKey() && _owner.getValue() == this)
_code = _decCode = JavaTypes.OID;
return false;
}
/**
* Resolve the declared type.
*/
private void resolveDeclaredType(Class type) {
ClassMetaData meta = _owner.getRepository().getMetaData(type,
_owner.getDefiningMetaData().getEnvClassLoader(), false);
if (meta != null)
_decCode = JavaTypes.PC;
if (meta != null && meta.isEmbeddedOnly() && !meta.isAbstract())
setEmbedded(true);
if (!isEmbedded())
_decTypeMeta = meta;
}
@Override
public void copy(ValueMetaData vmd) {
// copy declared types, but if OID revert to PC until we resolve
// to OID ourselves
_decType = vmd.getDeclaredType();
_decCode = vmd.getDeclaredTypeCode();
if (_decCode == JavaTypes.OID)
_decCode = JavaTypes.PC;
_delete = vmd.getCascadeDelete();
_persist = vmd.getCascadePersist();
_attach = vmd.getCascadeAttach();
_detach = vmd.getCascadeDetach();
_refresh = vmd.getCascadeRefresh();
_typeOverride = vmd.getTypeOverride();
_serialized = vmd.isSerialized();
if (_embeddedMeta != null)
_embeddedMeta.setDescribedType(vmd.getDeclaredType());
// don't allow copy to override embedded
if (_embedded == null)
setEmbedded(vmd.isEmbedded());
}
}