package org.openntf.domino.nsfdata.impldxl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.openntf.domino.nsfdata.NSFItem;
import org.openntf.domino.nsfdata.NSFMimeData;
import org.openntf.domino.nsfdata.NSFNote;
import org.openntf.domino.nsfdata.NSFRichTextData;
import org.openntf.domino.nsfdata.impldxl.item.DXLItemComposite;
import org.openntf.domino.nsfdata.impldxl.item.DXLItemFactory;
import org.openntf.domino.nsfdata.structs.cd.CDFILEHEADER;
import org.openntf.domino.nsfdata.structs.cd.CDFILESEGMENT;
import org.openntf.domino.nsfdata.structs.cd.CDRecord;
import org.openntf.domino.nsfdata.structs.cd.CData;
import org.openntf.domino.utils.xml.XMLNode;
public class DXLNote implements NSFNote, Serializable {
private static final long serialVersionUID = 1L;
private static final boolean DEBUG = false;
private final NoteClass noteClass_;
private final int noteId_;
private final String universalId_;
private final boolean default_;
private final int sequence_;
private final List<NSFItem> items_ = new ArrayList<NSFItem>();
private transient Map<String, List<NSFItem>> itemsByName_ = new TreeMap<String, List<NSFItem>>(String.CASE_INSENSITIVE_ORDER);
public static DXLNote create(final XMLNode node) {
return new DXLNote(node);
}
private DXLNote(final XMLNode node) {
String noteClass = node.getAttribute("class");
if ("replicationformula".equalsIgnoreCase(noteClass)) {
noteClass = "replformula";
} else if ("helpindex".equalsIgnoreCase(noteClass)) {
noteClass = "help_index";
}
noteClass_ = NoteClass.valueOf(noteClass.toUpperCase());
default_ = "true".equals(node.getAttribute("default"));
XMLNode noteInfo = node.selectSingleNode("noteinfo");
noteId_ = Integer.parseInt(noteInfo.getAttribute("noteid"), 16);
universalId_ = noteInfo.getAttribute("unid");
sequence_ = Integer.parseInt(noteInfo.getAttribute("sequence"), 10);
if (DEBUG)
System.out.println("\tUNID: " + universalId_);
for (XMLNode itemNode : node.selectNodes("./item")) {
if (DEBUG)
System.out.println("\tItem: " + itemNode.getAttribute("name"));
// Find out whether this is a duplicate item - solo items are all 0, while
// dups are 1-based. I am aware that this is horrible.
String name = itemNode.getAttribute("name");
boolean duplicate = node.selectNodes("./item[@name='" + name + "']").size() > 1;
int dupItemId = duplicate ? (getItems(name).size() + 1) : 0;
NSFItem item = DXLItemFactory.create(itemNode, dupItemId);
if (item != null) {
items_.add(item);
if (!itemsByName_.containsKey(name)) {
itemsByName_.put(name, new ArrayList<NSFItem>());
}
itemsByName_.get(name).add(item);
if (DEBUG) {
System.out.print("\t\t[" + item.getType());
System.out.print(", Class: " + item.getClass().getSimpleName());
System.out.print(", Dup ID: " + item.getDupItemId());
System.out.print(", Value: " + item.getValue());
System.out.println("]");
// Output composite data
if (item instanceof DXLItemComposite) {
CData cdata = ((DXLItemComposite) item).getValue();
int breaker = 0;
for (CDRecord record : cdata) {
if (breaker++ > 1000) {
System.out.println("we went too deep!");
break;
}
System.out.print("\t\t\t[Signature: " + record.getHeader());
System.out.print(", Length: " + record.getHeader().getRecordLength());
System.out.print(", Value: " + record);
System.out.println("]");
}
}
}
}
}
}
@Override
public NoteClass getNoteClass() {
return noteClass_;
}
@Override
public int getNoteId() {
return noteId_;
}
@Override
public String getUniversalId() {
return universalId_;
}
@Override
public boolean isDefault() {
return default_;
}
@Override
public int getSequence() {
return sequence_;
}
@Override
public Collection<NSFItem> getItems(final String itemName) {
List<NSFItem> items = itemsByName_.get(itemName);
if (items != null) {
return items;
} else {
return Collections.emptyList();
}
}
@Override
public Collection<NSFItem> getItems() {
return Collections.unmodifiableList(items_);
}
@Override
public NSFMimeData getMimeData(final String itemName) {
// TODO Implement this
return null;
}
@Override
public NSFRichTextData getRichText(final String itemName) {
// TODO Implement this
return null;
}
@Override
public boolean hasItem(final String itemName) {
return !getItems(itemName).isEmpty();
}
// TODO Make sure it works with multiple files
@Override
public void extractFileResource(final String itemName, final java.io.OutputStream os) {
Collection<NSFItem> items = getItems(itemName);
int segmentCount = 0;
int totalSegments = 0;
for (NSFItem item : items) {
if (item instanceof DXLItemComposite) {
CData cdata = ((DXLItemComposite) item).getValue();
int breaker = 0;
for (CDRecord record : cdata) {
if (breaker++ > 1000) {
System.out.println("we went too deep!");
break;
}
if (record instanceof CDFILEHEADER) {
CDFILEHEADER header = (CDFILEHEADER) record;
totalSegments = (int) header.SegCount.get();
segmentCount = 0;
}
if (record instanceof CDFILESEGMENT) {
CDFILESEGMENT seg = (CDFILESEGMENT) record;
ByteBuffer data = ByteBuffer.wrap(seg.getFileData());
data.order(ByteOrder.LITTLE_ENDIAN);
try {
os.write(data.array(), data.position(), data.limit() - data.position());
} catch (IOException e) {
throw new RuntimeException(e);
}
if (++segmentCount >= totalSegments) {
try {
os.flush();
return;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
itemsByName_ = new TreeMap<String, List<NSFItem>>(String.CASE_INSENSITIVE_ORDER);
for (NSFItem item : items_) {
String itemName = item.getName();
if (!itemsByName_.containsKey(itemName)) {
itemsByName_.put(itemName, new ArrayList<NSFItem>());
}
itemsByName_.get(itemName).add(item);
}
}
}