/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.dtree;
import java.io.DataOutput;
import java.io.IOException;
import org.eclipse.core.runtime.*;
/**
* Class for writing a single data tree (no parents) to an output stream.
*/
public class DataTreeWriter {
/**
* Callback for serializing tree data
*/
protected IDataFlattener flatener;
/**
* The stream to write output to
*/
protected DataOutput output;
/**
* Constant representing infinite recursion depth
*/
public static final int D_INFINITE= -1;
/**
* Creates a new DeltaTreeWriter.
*/
public DataTreeWriter(IDataFlattener f) {
flatener= f;
}
/**
* Writes the subtree rooted at the given node.
*
* @param node The subtree to write.
* @param path The path of the current node.
* @param depth The depth of the subtree to write.
*/
protected void writeNode(AbstractDataTreeNode node, IPath path, int depth) throws IOException {
int type= node.type();
/* write the node name */
String name= node.getName();
if (name == null) {
name= ""; //$NON-NLS-1$
}
output.writeUTF(name);
/* write the node type */
writeNumber(type);
/* maybe write the data */
if (node.hasData()) {
Object data= node.getData();
/**
* Write a flag indicating whether or not the data field is null. Zero means data is
* null, non-zero means data is present
*/
if (data == null) {
writeNumber(0);
} else {
writeNumber(1);
flatener.writeData(path, node.getData(), output);
}
}
/* maybe write the children */
if (depth > 0 || depth == D_INFINITE) {
AbstractDataTreeNode[] children= node.getChildren();
/* write the number of children */
writeNumber(children.length);
/* write the children */
int newDepth= (depth == D_INFINITE) ? D_INFINITE : depth - 1;
for (int i= 0, imax= children.length; i < imax; i++) {
writeNode(children[i], path.append(children[i].getName()), newDepth);
}
} else {
/* write the number of children */
writeNumber(0);
}
}
/**
* Writes an integer in a compact format biased towards small non-negative numbers. Numbers
* between 0 and 254 inclusive occupy 1 byte; other numbers occupy 5 bytes.
*/
protected void writeNumber(int number) throws IOException {
if (number >= 0 && number < 0xff) {
output.writeByte(number);
} else {
output.writeByte(0xff);
output.writeInt(number);
}
}
/**
* Writes a single node to the output. Does not recurse on child nodes, and does not write the
* number of children.
*/
protected void writeSingleNode(AbstractDataTreeNode node, IPath path) throws IOException {
/* write the node name */
String name= node.getName();
if (name == null) {
name= ""; //$NON-NLS-1$
}
output.writeUTF(name);
/* write the node type */
writeNumber(node.type());
/* maybe write the data */
if (node.hasData()) {
Object data= node.getData();
/**
* Write a flag indicating whether or not the data field is null. Zero means data is
* null, non-zero means data is present
*/
if (data == null) {
writeNumber(0);
} else {
writeNumber(1);
flatener.writeData(path, node.getData(), output);
}
}
}
/**
* Writes the given AbstractDataTree to the given stream. This writes a single DataTree or
* DeltaDataTree, ignoring parent trees.
*
* @param path Only writes data for the subtree rooted at the given path, and for all nodes
* directly between the root and the subtree.
* @param depth In the subtree rooted at the given path, only write up to this depth. A depth of
* infinity is given by the constant D_INFINITE.
*/
public void writeTree(AbstractDataTree tree, IPath path, int depth, DataOutput output) throws IOException {
this.output= output;
/* tunnel down relevant path */
AbstractDataTreeNode node= tree.getRootNode();
IPath currentPath= Path.ROOT;
String[] segments= path.segments();
for (int i= 0; i < segments.length; i++) {
String nextSegment= segments[i];
/* write this node to the output */
writeSingleNode(node, currentPath);
currentPath= currentPath.append(nextSegment);
node= node.childAtOrNull(nextSegment);
/* write the number of children for this node */
if (node != null) {
writeNumber(1);
} else {
/* can't navigate down the path, just give up with what we have so far */
writeNumber(0);
return;
}
}
Assert.isTrue(currentPath.equals(path), "dtree.navigationError"); //$NON-NLS-1$
/* recursively write the subtree we're interested in */
writeNode(node, path, depth);
}
}