com.quinsoft.zeidon.standardoe.WriteOiToPorStream Maven / Gradle / Ivy
/**
This file is part of the Zeidon Java Object Engine (Zeidon JOE).
Zeidon JOE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zeidon JOE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Zeidon JOE. If not, see .
Copyright 2009-2015 QuinSoft
*/
/**
*
*/
package com.quinsoft.zeidon.standardoe;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import com.quinsoft.zeidon.Blob;
import com.quinsoft.zeidon.SerializeOi;
import com.quinsoft.zeidon.StreamWriter;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.WriteOiFlags;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.objectdefinition.AttributeDef;
import com.quinsoft.zeidon.objectdefinition.EntityDef;
import com.quinsoft.zeidon.objectdefinition.InternalType;
import com.quinsoft.zeidon.utils.JoeUtils;
import com.quinsoft.zeidon.utils.PortableFileReader;
/**
* Writes an object instance to a stream using Zeidon's portable file format.
*/
public class WriteOiToPorStream implements StreamWriter
{
private static final long META_OI_LOCKED = 0x00000001;
private static final long META_OI_READONLY = 0x00000002;
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyy/MM/dd HH:mm:ss");
private ViewImpl view;
private Writer writer;
private EnumSet flags;
private SerializeOi options;
@Override
public void writeToStream( SerializeOi options, Writer writer )
{
List viewList = options.getViewList();
if ( viewList.size() > 1 )
throw new ZeidonException( "POR stream processing can only handle a single OI" );
view = ((InternalView) viewList.get( 0 ) ).getViewImpl();
this.writer = writer;
flags = options.getFlags();
this.options = options;
writeToStream();
}
private void write( String buffer ) throws IOException
{
writer.write( buffer );
}
private void write(String control, Object...objects ) throws IOException
{
writer.write( JoeUtils.format( control, objects ) );
}
private void writeln() throws IOException
{
writer.write( "\n" );
}
private void writeln(String control, Object...objects ) throws IOException
{
write( JoeUtils.format( control, objects ) );
writeln();
}
void writeToStream()
{
// Compile a regex that will search for special printable chars later on.
Pattern specialChars = Pattern.compile( ".*[\\n\\r" + PortableFileReader.STRING_STORED_AS_BLOB_REGEX + "]+.*", Pattern.DOTALL );
// Since we use it a lot, create a local value.
boolean writeIncremental = flags.contains( WriteOiFlags.INCREMENTAL );
// Used to create the link statements at the end.
int hierIndex = 0;
// Initialize instance flags that we'll use during the write.
for ( EntityInstanceImpl ei : view.getObjectInstance().getEntities() )
{
ei.setWritten( false );
ei.setRecordOwner( false );
ei.setHierIndex( -1 );
}
long lastLinked = -1;
String erDate = "0";
String incremental = writeIncremental ? "1" : "0";
String compressed = "0";
String optimisticOIs = "0";
String attribFlags = writeIncremental ? "1" : "0";
String name = view.getLodDef().getName();
if ( writer instanceof FileWriter )
name = options.getResourceName();
String header = String.format( "z%s%s%s%s%sZeidon %8s %s %s",
erDate, incremental, compressed, optimisticOIs, attribFlags,
name, view.getLodDef().getName(),
DATE_FORMATTER.print( new DateTime() ) );
try
{
writeln( header );
if ( writeIncremental )
{
long flags = 0;
if ( view.getObjectInstance().isLocked() )
flags |= META_OI_LOCKED;
if ( view.getObjectInstance().isReadOnly() )
flags |= META_OI_READONLY;
writeln("mOIFLAGS %x", flags);
}
// Loop through the entities. We can't use the iterator because the inner loop
// object may skip some.
for ( EntityInstanceImpl ei = view.getObjectInstance().getRootEntityInstance();
ei != null;
ei = ei.getNextHier() )
{
EntityDef entityDef = ei.getEntityDef();
if ( ei.isHidden() && ! writeIncremental )
{
// EI is hidden and we're not writing incrementals, so skip this one
// and all its children.
ei = ei.getLastChildHier();
continue;
}
ei.setHierIndex( hierIndex++ );
// Write out entity name and instance flags.
write( "e%-9s %d", entityDef.getName(), ei.getDepth() );
if ( writeIncremental )
{
// Write the incremental flags.
write( ",%d", ei.getInstanceFlags() );
}
writeln();
if ( flags.contains( WriteOiFlags.ENTITY_TAGS ) || ei.getTag() != null )
{
String tag = ei.getTag();
if ( StringUtils.isBlank( tag ) )
tag = Integer.toHexString( ei.hashCode() );
writeln( "mETAG %s", tag );
}
if ( flags.contains( WriteOiFlags.ENTITY_KEYS ) )
{
writeln( "mEKEY %d", ei.getEntityKey() );
}
// If the EI has already been written (this means it's linked to another
// EI that has already been written) and it has no non-persist record,
// then we don't need to write it's attribute values.
if ( ei.isWritten() )
{
lastLinked = ei.getHierIndex();
ei.setWritten( true );
}
else
{
// The ei is linked and it hasn't been written so it must be the record
// owner.
ei.setRecordOwner( true );
// Set the written flag for all the linked instances that belong
// to this OI.
for ( EntityInstanceImpl linked : ei.getAllLinkedInstances() )
{
if ( linked.getObjectInstance() == view.getObjectInstance() )
linked.setWritten( true );
}
}
// Loops through all non-null attributes.
for ( AttributeDef AttributeDef : ei.getNonNullAttributeList() )
{
// Don't bother if the attribute is derived.
if ( AttributeDef.isDerived() )
continue;
if ( flags.contains( WriteOiFlags.KEYS_ONLY ) && ! AttributeDef.isKey() )
continue;
// If this entity is the one that was most recently flagged as linked, don't
// write persistent attributes -- they were already written for a linked
// instance.
if ( AttributeDef.isPersistent() && ei.getHierIndex() == lastLinked )
continue;
// Write the attribute flags if they aren't 0.
String flags = "";
if ( writeIncremental && ei.getInternalAttribute( AttributeDef ).getAttributeFlags() != 0 )
flags = String.format(",%x", ei.getInternalAttribute( AttributeDef ).getAttributeFlags() );
write("a%-9s ", AttributeDef.getName() + flags);
if ( AttributeDef.getType() == InternalType.BLOB )
{
Blob blob = (Blob) ei.getAttribute( AttributeDef ).getValue();
byte[] bytes = blob.getBytes();
writeln("%d", bytes.length );
write( bytes.toString() );
}
else
{
String value = ei.getAttribute( AttributeDef ).getString();
// If the attribute type is a string then check to see if it contains "special"
// characters that interfere with normal attribute values, like "\n".
if ( AttributeDef.getType() == InternalType.STRING &&
( value.length() > 254 || specialChars.matcher( value ).matches() ) )
{
writeln("%c%d", PortableFileReader.STRING_STORED_AS_BLOB, value.length() );
}
writeln("%s", value );
}
} // for each attribute...
// Write a blank line just to look pretty.
writeln();
} // for each entity instance...
// If any intra-object linked instances were found, create
// link records now.
if ( lastLinked > -1 )
{
for ( EntityInstanceImpl ei : view.getObjectInstance().getEntities() )
{
// If we've gone past the last linked EI we're done.
if ( ei.getHierIndex() > lastLinked )
break;
// If index is -1 it wasn't written.
if ( ei.getHierIndex() == -1 )
continue;
// If the entity is the record owner then we don't write link cards.
// Link records are written for the non-record owner.
if ( ei.isRecordOwner() )
continue;
synchronized ( ei.getAllLinkedInstances() )
{
for ( EntityInstanceImpl linked : ei.getAllLinkedInstances() )
{
if ( linked == ei )
continue; // Don't write a link record for ourself.
if ( linked.getObjectInstance() == view.getObjectInstance() &&
linked.isRecordOwner() )
{
assert ei.getHierIndex() != linked.getHierIndex() : "Mismatched record owners.";
assert ei.getEntityDef().getErEntityToken() == linked.getEntityDef().getErEntityToken() :
"Mismatched entity tokens";
writeln("i%-9d %d", ei.getHierIndex(), linked.getHierIndex() );
break;
}
}
}
} // for each entity instance...
} // if ( lastLinked > -1 )...
// Indicate that the OI is done.
writeln( "ZEND" );
}
catch ( Throwable e )
{
throw ZeidonException.wrapException( e );
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy