/* A reference to an archive resolvable by an ArchiveResolver. e.g. a Freenet URI. * * Copyright (C) 2010, 2011 Darrell Karbott * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.0 of the License, or (at your option) any later version. * * This library 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 * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks * * This file was developed as component of * "fniki" (a wiki implementation running over Freenet). */ package wormarc; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.InputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; public final class ExternalRefs { public final static class Reference implements Comparable<Reference> { public final int mKind; public final String mExternalKey; public Reference(int kind, String key) { mKind = kind; mExternalKey = key; if (mExternalKey == null) { throw new IllegalArgumentException("Key is null"); } if (mKind < 0 || mKind > 127) { throw new IllegalArgumentException("kind out of range [0, 127]"); } if (mExternalKey.length() > 32767) { throw new IllegalArgumentException("Key too big."); } } // IMPORTANT: Must be able to sort stably so you get an identical binary rep for the same list. public int compareTo(Reference other) { if (mKind - other.mKind == 0) { // Then by key string. return mExternalKey.compareTo(other.mExternalKey); } // First by kind. return mKind - other.mKind; } } public final List<Reference> mRefs; public final static int KIND_LOCAL = 1; public final static int KIND_FREENET = 2; public final static Reference CURRENT_ARCHIVE = new Reference(KIND_LOCAL, "current_archive"); // Like hg rev -1. i.e. the rev before the first. public final static Reference NULL_ARCHIVE = new Reference(KIND_LOCAL, "null_archive"); public final static ExternalRefs NONE = new ExternalRefs(new ArrayList<Reference>()); ExternalRefs(List<Reference> refs) { Collections.sort(refs); // To get the same serialed rep. mRefs = Collections.unmodifiableList(refs); if (mRefs.size() > 127) { throw new IllegalArgumentException("Too many refs."); } } public InputStream toBytes() throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(buffer); outputStream.writeByte(mRefs.size()); for (Reference ref : mRefs) { outputStream.writeByte(ref.mKind); byte[] raw = ref.mExternalKey.getBytes(IOUtil.UTF8); outputStream.writeShort(raw.length); outputStream.write(raw); } return new ByteArrayInputStream(buffer.toByteArray()); } public static ExternalRefs fromBytes(InputStream rawBytes) throws IOException { DataInputStream inputStream = new DataInputStream(rawBytes); int count = inputStream.readByte(); if (count > 127) { throw new IOException("Parse Error: count out of range [0, 127]"); } List<Reference> refs = new ArrayList<Reference>(); while (count > 0) { int kind = inputStream.readByte(); if (count < 0 || count > 127) { throw new IOException("Parse Error: kind out of range [0, 127]"); } int rawLength = inputStream.readShort(); if (rawLength < 0 || rawLength > 32767) { throw new IOException("Parse Error: keyLength out of range [0, 32767]"); } byte[] raw = new byte[rawLength]; inputStream.readFully(raw); String key = new String(raw, IOUtil.UTF8); refs.add(new Reference(kind, key)); count--; } return new ExternalRefs(refs); } public String pretty(String labelWithTrailingSpace) { StringBuilder buffer = new StringBuilder(); buffer.append(String.format("--- %sExternalRefs ---\n", labelWithTrailingSpace)); for (Reference ref: mRefs) { buffer.append(String.format(" [%d]:%s\n", ref.mKind, ref.mExternalKey)); } buffer.append("---"); return buffer.toString(); } public String pretty() { return pretty(""); } public static ExternalRefs create(List<String> keys, int kind) { List<Reference> refs = new ArrayList<Reference>(); for (String key : keys) { refs.add(new Reference(kind, key)); } return new ExternalRefs(refs); } }