All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quinsoft.zeidon.standardoe.WriteOiToPorStream Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/**
    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