package com.limegroup.gnutella.messages;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.limewire.io.BadGGEPBlockException;
import org.limewire.io.GGEP;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.UrnSet;
/**
* Encapsulation of a HUGE block. Offers various get methods to retrieve its
* contents, and handles parsing, etc.
*/
public class HUGEExtension {
// the disparate types of objects encoded in a HUGE extension - one set per
// (lazily constructed)
// -----------------------------------------
private GGEP _ggep = null;
private Set<URN> _urns = null;
private Set<URN.Type> _urnTypes = null;
private Set<String> _miscBlocks = null;
// -----------------------------------------
private List<GGEPBlock> _ggepBlocks = null;
/**
* @return the merged GGEP of all GGEPs in this HUGE extension or null
* if no GGEPs were found
*/
public GGEP getGGEP() {
return _ggep;
}
/**
* Returns unmodifiable list of GGEP blocks.
* @return empty list if there no GGEP blocks were found
*/
public List<GGEPBlock> getGGEPBlocks() {
if (_ggepBlocks == null) {
return Collections.emptyList();
}
else {
return Collections.unmodifiableList(_ggepBlocks);
}
}
/** @return the set of URN Objects in this HUGE extension.
*/
public Set<URN> getURNS() {
if (_urns == null)
return Collections.emptySet();
else
return _urns;
}
/** @return the set of URN Type Objects in this HUGE extension.
*/
public Set<URN.Type> getURNTypes() {
if (_urnTypes == null)
return Collections.emptySet();
else
return _urnTypes;
}
/** @return the set of miscellaneous blocks (Strings) in this extension.
*/
public Set<String> getMiscBlocks() {
if (_miscBlocks == null)
return Collections.emptySet();
else
return _miscBlocks;
}
public HUGEExtension(byte[] extsBytes) {
int currIndex = 0;
// while we don't encounter a null....
while ((currIndex < extsBytes.length) &&
(extsBytes[currIndex] != (byte)0x00)) {
// HANDLE GGEP STUFF
if (extsBytes[currIndex] == GGEP.GGEP_PREFIX_MAGIC_NUMBER) {
int start = currIndex;
int[] endIndex = new int[1];
endIndex[0] = currIndex+1;
try {
GGEP ggep = new GGEP(extsBytes, currIndex, endIndex);
if (_ggep == null) {
_ggep = new GGEP();
}
_ggep.merge(ggep);
if (_ggepBlocks == null) {
_ggepBlocks = new ArrayList<GGEPBlock>(2);
}
_ggepBlocks.add(new GGEPBlock(ggep, start, endIndex[0]));
} catch (BadGGEPBlockException ignored) {}
currIndex = endIndex[0];
} else { // HANDLE HUGE STUFF
int delimIndex = currIndex;
while ((delimIndex < extsBytes.length)
&& (extsBytes[delimIndex] != (byte)0x1c))
delimIndex++;
if (delimIndex <= extsBytes.length) {
try {
// another GEM extension
String curExtStr = new String(extsBytes, currIndex,
delimIndex - currIndex,
"UTF-8");
if (URN.isUrn(curExtStr)) {
// it's an URN to match, of form "urn:namespace:etc"
URN urn = URN.createSHA1Urn(curExtStr);
if(_urns == null)
_urns = new UrnSet();
_urns.add(urn);
} else if (URN.Type.isSupportedUrnType(curExtStr)) {
if(_urnTypes == null)
_urnTypes = EnumSet.noneOf(URN.Type.class);
_urnTypes.add(URN.Type.createUrnType(curExtStr));
} else {
// miscellaneous, but in the case of queries, xml
if (_miscBlocks == null)
_miscBlocks = new HashSet<String>(1);
_miscBlocks.add(curExtStr);
}
} catch (IOException bad) {}
} // else we've overflown and not encounted a 0x1c - discard
currIndex = delimIndex+1;
}
}
}
/**
* Represents a single ggep block in the HUGE extension block.
*/
public static class GGEPBlock {
private int start;
private int end;
private GGEP ggep;
public GGEPBlock(GGEP ggep, int start, int end) {
this.ggep = ggep;
this.start = start;
this.end = end;
}
public GGEP getGGEP() {
return ggep;
}
public int getStartPos() {
return start;
}
public int getEndPos() {
return end;
}
}
}