blob: 82f24a56fda2192f299e873336eff273f5dd8d25 [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.persist.impl;
import java.lang.reflect.Array;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import com.sleepycat.persist.model.EntityModel;
import com.sleepycat.persist.model.PersistentProxy;
import com.sleepycat.persist.raw.RawObject;
/**
* Format for types proxied by a PersistentProxy.
*
* @author Mark Hayes
*/
public class ProxiedFormat extends Format {
private static final long serialVersionUID = -1000032651995478768L;
private Format proxyFormat;
private transient String proxyClassName;
ProxiedFormat(Catalog catalog, Class proxiedType, String proxyClassName) {
super(catalog, proxiedType);
this.proxyClassName = proxyClassName;
}
/**
* Returns the proxy class name. The proxyClassName field is non-null for
* a constructed object and null for a de-serialized object. Whenever the
* proxyClassName field is null (for a de-serialized object), the
* proxyFormat will be non-null.
*/
private String getProxyClassName() {
if (proxyClassName != null) {
return proxyClassName;
} else {
assert proxyFormat != null;
return proxyFormat.getClassName();
}
}
/**
* In the future if we implement container proxies, which support nested
* references to the container, then we will return false if this is a
* container proxy. [#15815]
*/
@Override
boolean areNestedRefsProhibited() {
return true;
}
@Override
void collectRelatedFormats(Catalog catalog,
Map<String, Format> newFormats) {
/* Collect the proxy format. */
assert proxyClassName != null;
catalog.createFormat(proxyClassName, newFormats);
}
@Override
void initialize(Catalog catalog, EntityModel model, int initVersion) {
/* Set the proxy format for a new (never initialized) format. */
if (proxyFormat == null) {
assert proxyClassName != null;
proxyFormat = catalog.getFormat(proxyClassName);
}
/* Make the linkage from proxy format to proxied format. */
proxyFormat.setProxiedFormat(this);
}
@Override
Object newArray(int len) {
return Array.newInstance(getType(), len);
}
@Override
public Object newInstance(EntityInput input, boolean rawAccess)
throws RefreshException {
Reader reader = proxyFormat.getReader();
if (rawAccess) {
return reader.newInstance(null, true);
} else {
/*
* Note that the read object will not be a PersistentProxy if
* a class converter mutation is used. In this case, the reader
* will be ConverterReader. ConverterReader.readObject
* will call ProxiedFormat.convertRawObject, which will call
* PersistentProxy.convertProxy to convert the proxy. So we do not
* need another call to the convertProxy method. [#19312]
*/
Object o = reader.readObject(reader.newInstance(null, false),
input, false);
if (o instanceof PersistentProxy) {
o = ((PersistentProxy) o).convertProxy();
}
return o;
}
}
@Override
public Object readObject(Object o, EntityInput input, boolean rawAccess)
throws RefreshException {
if (rawAccess) {
o = proxyFormat.getReader().readObject(o, input, true);
}
/* Else, do nothing here -- newInstance reads the value. */
return o;
}
@Override
void writeObject(Object o, EntityOutput output, boolean rawAccess)
throws RefreshException {
if (rawAccess) {
proxyFormat.writeObject(o, output, true);
} else {
PersistentProxy proxy =
(PersistentProxy) proxyFormat.newInstance(null, false);
proxy.initializeProxy(o);
proxyFormat.writeObject(proxy, output, false);
}
}
@Override
Object convertRawObject(Catalog catalog,
boolean rawAccess,
RawObject rawObject,
IdentityHashMap converted)
throws RefreshException {
PersistentProxy proxy = (PersistentProxy) proxyFormat.convertRawObject
(catalog, rawAccess, rawObject, converted);
Object o = proxy.convertProxy();
converted.put(rawObject, o);
return o;
}
@Override
void skipContents(RecordInput input)
throws RefreshException {
proxyFormat.skipContents(input);
}
@Override
void copySecMultiKey(RecordInput input, Format keyFormat, Set results)
throws RefreshException {
CollectionProxy.copyElements(input, this, keyFormat, results);
}
@Override
boolean evolve(Format newFormatParam, Evolver evolver) {
if (!(newFormatParam instanceof ProxiedFormat)) {
/*
* A workaround for reading the BigDecimal data stored by
* BigDecimal proxy before je4.1.
*
* The BigDecimal proxy has a proxied format for BigDecimal, which
* is a built-in SimpleType. We will evolve this ProxiedFormat of
* BigDecimal to the SimpleFormat. In other words, the conversion
* from a BigDecimal proxied format to a BigDecimal SimpleFormat is
* allowed, and the old format can be used as the reader of the old
* data.
*/
if (newFormatParam.allowEvolveFromProxy()) {
evolver.useEvolvedFormat(this, this, newFormatParam);
return true;
}
evolver.addEvolveError
(this, newFormatParam, null,
"A proxied class may not be changed to a different type");
return false;
}
ProxiedFormat newFormat = (ProxiedFormat) newFormatParam;
if (!evolver.evolveFormat(proxyFormat)) {
return false;
}
Format newProxyFormat = proxyFormat.getLatestVersion();
if (!newProxyFormat.getClassName().equals
(newFormat.getProxyClassName())) {
evolver.addEvolveError
(this, newFormat, null,
"The proxy class for this type has been changed from: " +
newProxyFormat.getClassName() + " to: " +
newFormat.getProxyClassName());
return false;
}
if (newProxyFormat != proxyFormat) {
evolver.useEvolvedFormat(this, this, newFormat);
} else {
evolver.useOldFormat(this, newFormat);
}
return true;
}
}