blob: b11f30c7ebdef2461084fb24148f01086d384510 [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation.
*
* 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 org.apache.jdo.impl.fostore;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.netbeans.mdr.persistence.Streamable;
import org.netbeans.mdr.persistence.StorageException;
import org.netbeans.mdr.persistence.StorageIOException;
/**
* Represents a class stored in the database.
* <p>
* This class is <code>public</code> so that it can be used as a
* <code>Streamable</code> and stored in the database.
*
* @author Dave Bristor
*/
public class DBClass implements Streamable {
// These are not final, because we must implement Streamable to store
// DBClass instances in the btree, and later read them out.
/** Fully qualified name of the class represented. */
private String name;
/** CLID corresponding to the class. It might be provisional, until the
* finishing phase of request handling has run. */
private CLID clid;
/** FSUID corresponding to the class. */
private FOStoreSchemaUID fsuid;
/** Lists the class's PersistenceCapable superclasses, from the class to
* Object. Null unless the class has PersistenceCapable superclasses. */
private ClassDetail supers[] = null;
/** The fields of the class. Null unless the class has fields. */
private ClassDetail fields[] = null;
/** Indicates whether the CLIDs of any superclass or field were given as
* provisional. */
private transient boolean hasProvisionals = false;
/** Logger */
static final Log logger = LogFactory.getFactory().getInstance(
"org.apache.jdo.impl.fostore"); // NOI18N
/** A ClassDetail represents a single field or PC superclass of a class
* that is (or was at one time) stored in the databse. When created, the
* given CLID might be provisional. If so, it will be replaced with a
* real CLID during ActivateClassHandler's finish phase.
* @see ActivateClassHandler#handleRequest
*/
class ClassDetail {
/** Name of the field or superclass. */
final String name;
/** CLID of the field or superclass. Not final, because it may be
* provisional when the ClassDetail is created, and updated later
* during finishing. */
CLID clid;
/* FSUID of the field or superclass. */
final FOStoreSchemaUID fsuid;
ClassDetail(DataInput in) throws IOException {
name = in.readUTF();
clid = CLID.read(in);
fsuid = FOStoreSchemaUID.read(in);
if (logger.isDebugEnabled()) {
logger.debug(
"DBClass: fieldName=" + name + ", " + clid); // NOI18N
}
}
void write(DataOutputStream dos) throws IOException {
dos.writeUTF(name);
clid.write(dos);
fsuid.write(dos);
}
/**
* If the current clid is provisional, replace it with a real one if
* possible.
*/
void remap(FOStoreDatabase db) {
if (clid.isProvisional()) {
CLID rCLID = db.getRealCLIDFromProvisional(clid);
if (null != rCLID) {
clid = rCLID;
if (logger.isDebugEnabled()) {
logger.debug(
"ClassDetail.remap: " + clid + " -> " + // NOI18N
rCLID);
}
}
}
}
CLID getCLID() {
return clid;
}
void setCLID(CLID clid) {
this.clid = clid;
}
public String toString() {
return "'" + name + "' " + clid.toString(); // NOI18N
}
}
// end of class ClassDetail
/**
* Constructor
*/
private DBClass(String name, CLID clid, FOStoreSchemaUID fsuid,
DataInput in, FOStoreDatabase db) throws IOException{
this.name = name;
this.clid = clid;
this.fsuid = fsuid;
if (logger.isDebugEnabled()) {
logger.debug("DBClass: " + name + ", " + clid // NOI18N
+ ", fsuid: " + fsuid); // NOI18N
}
int numSupers = in.readInt();
if (numSupers > 0) {
supers = new ClassDetail[numSupers];
getClassDetail(supers, in);
}
int numFields = in.readInt();
if (numFields > 0) {
fields = new ClassDetail[numFields];
getClassDetail(fields, in);
}
}
/**
* @see ActivateClassRequest#doRequestBody
*/
static DBClass create(String name, CLID clid, FOStoreSchemaUID fsuid,
DataInput in, FOStoreDatabase db)
throws IOException{
return new DBClass(name, clid, fsuid, in, db);
}
/**
* Gets the details for as many slots as are in the given array.
*/
private void getClassDetail(ClassDetail details[], DataInput in)
throws IOException {
int length = details.length;
for (int i = 0; i < length; i++) {
ClassDetail detail = new ClassDetail(in);
details[i] = detail;
CLID detailCLID = detail.getCLID();
if (detailCLID.isProvisional()) {
hasProvisionals = true;
}
}
}
/**
* @return True if any of this DBClass's fields or superclasses has a CLID
* that is provisional.
*/
// In that case, ActivateClassHandler should cause remapCLIDs to be
// invoked during finishing.
boolean hasProvisionals() {
return hasProvisionals;
}
/**
* Changes the CLID mapping of this DBClass's fields and PC superclasses
* in the database as from provisional to real as necessary.
* @param db Database in which to find the provisional-to-real mapping.
*/
void remapCLIDs(FOStoreDatabase db) {
remapCLIDs(supers, db);
remapCLIDs(fields, db);
}
/**
* Changes provisional CLIDs to real CLIDs in the given ClassDetails.
*/
private void remapCLIDs(ClassDetail details[], FOStoreDatabase db) {
if (null != details && details.length > 0) {
int length = details.length;
for (int i = 0; i < length; i++) {
details[i].remap(db);
}
}
}
/**
* @return True if this DBClass has superclasses.
*/
// In that case, ActivateClassHandler should cause setupSubclasses to be
// invoked during finishing.
boolean hasSuperclasses() {
return null != supers;
}
/**
* Sets up subclass relationships between this DBClass and its superclass
* DBClass instances.
*/
// Invariants: all superclasses of this DBClass have already been
// processed, and this class *does* have superclasses.
void setupSubclasses(FOStoreDatabase db) throws FOStoreDatabaseException {
int length = supers.length;
for (int i = 0; i < length; i++) {
CLID superCLID = supers[i].getCLID();
if (logger.isDebugEnabled()) {
logger.debug("DBClass.setupSubclasses for " + clid + //NOI18N
" superclass: " + superCLID); // NOI18N
}
OID subclassSetOID = DBInfo.getSubclassSetOID(superCLID);
SubclassSet ss = (SubclassSet)db.getIfExists(subclassSetOID);
if (null == ss) {
ss = SubclassSet.create(subclassSetOID, clid);
} else {
ss.add(clid);
}
db.put(subclassSetOID, ss);
}
}
OID getOID() {
return DBInfo.getDBClassOID(clid);
}
CLID getCLID() {
return clid;
}
void setCLID(CLID clid) {
this.clid = clid;
}
String getName() {
return name;
}
FOStoreSchemaUID getFSUID() {
return fsuid;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("DBClass '").append(name).append("' "); // NOI18N
sb.append(clid.toString());
if (null != supers) {
for (int i = 0; i < supers.length; i++) {
sb.append("\n superclass " + supers[i].toString()); // NOI18N
}
}
if (null != fields) {
for (int i = 0; i < fields.length; i++) {
sb.append("\n field " + fields[i].toString()); // NOI18N
}
}
sb.append("\n"); // NOI18N
return sb.toString();
}
//
// Implement Streamable
//
public DBClass() { }
/**
* Write this DBClass to the given stream.
*/
public void write(OutputStream os) throws StorageException {
DataOutputStream dos = new DataOutputStream(os);
try {
dos.writeUTF(name);
clid.write(dos);
fsuid.write(dos);
writeClassDetails(supers, dos);
writeClassDetails(fields, dos);
} catch (IOException ex) {
throw new StorageIOException(ex);
}
}
/**
* Write the given details of this DBClass to the given stream.
*/
private void writeClassDetails(ClassDetail details[],
DataOutputStream dos) throws IOException {
if (null == details) {
dos.writeInt(0);
} else {
int length = details.length;
dos.writeInt(length);
for (int i = 0; i < length; i++) {
details[i].write(dos);
}
}
}
/**
* Initialize this DBClass from the given stream.
*/
public void read(InputStream is) throws StorageException {
DataInputStream dis = new DataInputStream(is);
try {
this.name = dis.readUTF();
this.clid = CLID.read(dis);
this.fsuid = FOStoreSchemaUID.read(dis);
int length = dis.readInt();
if (length > 0) {
supers = new ClassDetail[length];
readClassDetails(supers, dis);
}
length = dis.readInt();
if (length > 0) {
fields = new ClassDetail[length];
readClassDetails(fields, dis);
}
} catch (IOException ex) {
throw new StorageIOException(ex);
}
}
/**
* Read details from the given input stream.
*/
private void readClassDetails(ClassDetail details[],
DataInputStream dis) throws IOException {
int length = details.length;
for (int i = 0; i < length; i++) {
details[i] = new ClassDetail(dis);
}
}
}