/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2014 The eXist Project
* http://exist-db.org
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Id$
*/
package org.exist.dom.persistent;
import org.exist.numbering.NodeId;
import org.exist.storage.Signatures;
import org.exist.util.ByteConversion;
import org.exist.util.pool.NodePool;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import javax.xml.XMLConstants;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Persistent implementation of a DOM processing-instruction node.
*
* @author wolf
*/
public class ProcessingInstructionImpl extends StoredNode implements ProcessingInstruction {
public static final int LENGTH_TARGET_DATA = 4; //Sizeof int;
protected String target = null;
protected String data = null;
public ProcessingInstructionImpl() {
super(Node.PROCESSING_INSTRUCTION_NODE);
}
public ProcessingInstructionImpl(final NodeId nodeId, final String target, final String data) {
super(Node.PROCESSING_INSTRUCTION_NODE, nodeId);
this.target = target;
this.data = data;
}
public ProcessingInstructionImpl(final String target, final String data) {
this(null, target, data);
}
@Override
public void clear() {
super.clear();
target = null;
data = null;
}
/**
* Gets the target attribute of the ProcessingInstructionImpl object
*
* @return The target value
*/
@Override
public String getTarget() {
return target;
}
/**
* Sets the target attribute of the ProcessingInstructionImpl object
*
* @param target The new target value
*/
public void setTarget(final String target) {
this.target = target;
}
@Override
public String getNodeName() {
return target;
}
@Override
public String getLocalName() {
return target;
}
@Override
public String getNamespaceURI() {
return XMLConstants.NULL_NS_URI;
}
/**
* Gets the data attribute of the ProcessingInstructionImpl object
*
* @return The data value
*/
@Override
public String getData() {
return data;
}
/**
* Sets the data attribute of the ProcessingInstructionImpl object
*
* @param data The new data value
*/
@Override
public void setData(final String data) {
this.data = data;
}
/**
* ? @see org.w3c.dom.Node#getBaseURI()
*/
@Override
public String getBaseURI() {
final StoredNode parent = getParentStoredNode();
if(parent != null) {
return parent.getBaseURI();
} else {
return getOwnerDocument().getBaseURI();
}
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("<?");
buf.append(target);
buf.append(" ");
buf.append(data);
buf.append(" ?>");
return buf.toString();
}
/**
* Serializes a (persistent DOM) Processing Instruction to a byte array
*
* data = signature nodeIdUnitsLength nodeId targetLength target contentLength content
*
* signature = [byte] 0x40
*
* nodeIdUnitsLength = [short] (2 bytes) The number of units of the processing instruction's NodeId
* nodeId = {@see org.exist.numbering.DLNBase#serialize(byte[], int)}
*
* targetLength = [int] (4 bytes) The length of the target string in bytes
* target = jUtf8
*
* contentLength = [int] (4 bytes) The length of the data string in bytes
* content = jUtf8
*
* jUtf8 = {@see java.io.DataOutputStream#writeUTF(java.lang.String)}
*/
@Override
public byte[] serialize() {
final byte[] td = target.getBytes(UTF_8);
final byte[] dd = data.getBytes(UTF_8);
final int nodeIdLen = nodeId.size();
final byte[] d = new byte[td.length + dd.length + nodeIdLen + 7];
int pos = 0;
d[pos] = (byte) (Signatures.Proc << 0x5);
pos += LENGTH_SIGNATURE_LENGTH;
ByteConversion.shortToByte((short) nodeId.units(), d, pos);
pos += NodeId.LENGTH_NODE_ID_UNITS;
nodeId.serialize(d, pos);
pos += nodeIdLen;
ByteConversion.intToByte(td.length, d, pos);
pos += LENGTH_TARGET_DATA;
System.arraycopy(td, 0, d, pos, td.length);
pos += td.length;
System.arraycopy(dd, 0, d, pos, dd.length);
return d;
}
public static StoredNode deserialize(final byte[] data, final int start, final int len, final DocumentImpl doc, final boolean pooled) {
int pos = start;
pos += LENGTH_SIGNATURE_LENGTH;
final int dlnLen = ByteConversion.byteToShort(data, pos);
pos += NodeId.LENGTH_NODE_ID_UNITS;
final NodeId dln = doc.getBrokerPool().getNodeFactory().createFromData(dlnLen, data, pos);
final int nodeIdLen = dln.size();
pos += nodeIdLen;
final int l = ByteConversion.byteToInt(data, pos);
pos += LENGTH_TARGET_DATA;
final String target = new String(data, pos, l, UTF_8);
pos += l;
final String cdata = new String(data, pos, len - (pos - start), UTF_8);
//OK : we have the necessary material to build the processing instruction
final ProcessingInstructionImpl pi;
if(pooled) {
pi = (ProcessingInstructionImpl) NodePool.getInstance().borrowNode(Node.PROCESSING_INSTRUCTION_NODE);
} else {
pi = new ProcessingInstructionImpl();
}
pi.setTarget(target);
pi.data = cdata;
pi.setNodeId(dln);
return pi;
}
@Override
public boolean hasChildNodes() {
return false;
}
@Override
public int getChildCount() {
return 0;
}
@Override
public Node getFirstChild() {
return null;
}
}