/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.struct;
import io.netty.buffer.ByteBuf;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import divconq.hub.Hub;
import divconq.lang.Memory;
import divconq.lang.chars.Special;
import divconq.lang.op.FuncResult;
import divconq.schema.DataType;
import divconq.script.StackEntry;
import divconq.struct.builder.BuilderStateException;
import divconq.struct.builder.ICompositeOutput;
import divconq.struct.builder.JsonStreamBuilder;
import divconq.struct.builder.JsonMemoryBuilder;
import divconq.struct.serial.CompositeToBufferBuilder;
import divconq.xml.XElement;
/**
* DivConq uses a specialized type system that provides type consistency across services
* (including web services), database fields and stored procedures, as well as scripting.
*
* All scalars (including primitives) and composites (collections) are wrapped by some
* subclass of Struct. All composites are wrapped by a subclass of this class. See
* ListStruct and RecordStruct.
*
* TODO link to blog entries.
*
* @author Andy
*
*/
abstract public class CompositeStruct extends Struct implements ICompositeOutput {
public CompositeStruct() {
}
public CompositeStruct(DataType type) {
super(type);
}
/**
* A way to select a child or sub child structure similar to XPath but lightweight.
* Can select composites and scalars. Use a . or / delimiter.
*
* For example: "Toys.3.Name" called on "Person" Record means return the (Struct) name of the
* 4th toy in this person's Toys list.
*
* Cannot go up levels, or back to root. Do not start with a dot or slash as in ".People".
*
* @param path string holding the path to select
* @return selected structure if any, otherwise null
*/
public FuncResult<Struct> select(String path) {
FuncResult<Struct> log = new FuncResult<Struct>();
log.setResult(this.select(PathPart.parse(log, path)));
return log;
}
/**
* A way to select a child or sub child structure similar to XPath but lightweight.
* Can select composites and scalars. Use a . or / delimiter.
*
* For example: "Toys.3.Name" called on "Person" Record means return the (Struct) name of the
* 4th toy in this person's Toys list.
*
* Cannot go up levels, or back to root. Do not start with a dot or slash as in ".People".
*
* @param path parts of the path holding a list index or a field name
* @return selected structure if any, otherwise null
*/
abstract public Struct select(PathPart... path);
/**
* Does this collection have any items or fields
*
* @return true if no items or fields
*/
abstract public boolean isEmpty();
/**
* Remove all child fields or items.
*/
abstract public void clear();
@Override
public String toString() {
try {
JsonMemoryBuilder rb = new JsonMemoryBuilder();
this.toBuilder(rb);
return rb.getMemory().toString();
}
catch (Exception x) {
//
}
return null;
}
// TODO there may be more efficient ways to do this, just a quick way for now
@Override
public boolean equals(Object obj) {
if ((obj instanceof CompositeStruct) && this.toString().equals(((CompositeStruct)obj).toString()))
return true;
return super.equals(obj);
}
public String toPrettyString() {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
JsonStreamBuilder rb = new JsonStreamBuilder(ps, true);
this.toBuilder(rb);
return os.toString("UTF-8");
}
catch (Exception x) {
//
}
return null;
}
@Override
public boolean isNull() {
return false;
}
/**
* Convert the structure to Json and return in Memory (think StringBuilder in this usage).
*
* @return Memory holding JSON representation of this structure and all children
* @throws BuilderStateException if the structure is invalid then this exception arises
*/
public Memory toMemory() throws BuilderStateException { // TODO return funcresult
JsonMemoryBuilder rb = new JsonMemoryBuilder();
this.toBuilder(rb);
return rb.getMemory();
}
public void toSerial(ByteBuf buf) throws BuilderStateException {
CompositeToBufferBuilder rb = new CompositeToBufferBuilder(buf);
this.toBuilder(rb);
rb.write(Special.End);
}
public void toSerialMemory(Memory res) throws BuilderStateException {
ByteBuf buf = Hub.instance.getBufferAllocator().heapBuffer(16 * 1024, 16 * 1024 * 1024);
CompositeToBufferBuilder rb = new CompositeToBufferBuilder(buf);
this.toBuilder(rb);
rb.write(Special.End);
res.write(buf.array(), buf.arrayOffset(), buf.readableBytes());
buf.release();
}
public Memory toSerialMemory() throws BuilderStateException {
ByteBuf buf = Hub.instance.getBufferAllocator().heapBuffer(16 * 1024, 16 * 1024 * 1024);
CompositeToBufferBuilder rb = new CompositeToBufferBuilder(buf);
this.toBuilder(rb);
rb.write(Special.End);
Memory res = new Memory(buf.readableBytes());
res.write(buf.array(), buf.arrayOffset(), buf.readableBytes());
buf.release();
return res;
}
@Override
public void operation(StackEntry stack, XElement code) {
if ("Clear".equals(code.getName())) {
this.clear();
stack.resume();
return;
}
super.operation(stack, code);
}
}