protected TorrentMetaInfo (
        TorrentInfoSection info, String announce, List<Set<String>> announceList, DateTime creationDate,
        String comment, String createdBy)
    {
        if (announce == null | info == null)
            throw new NullPointerException();

        this.announce = announce;
        this.creationDate = creationDate;
        this.comment = comment == null ? "" : comment;
        this.createdBy = createdBy == null ? "" : createdBy;
        this.info = info;

        if (announceList == null) {
            this.announceList = new ArrayList<Set<String>>();
        } else {
            List<Set<String>> ual = new ArrayList<Set<String>>();
            for (Set<String> ls : announceList) {
                if (ls != null) {
                    Set<String> ns = new HashSet<String>(ls);
                    ns.remove(null);
                    ual.add(Collections.unmodifiableSet(ns));
                }
            }
            this.announceList = Collections.unmodifiableList(ual);
        }

        // hashCode
        cachedHashCode = announce.hashCode() ^ info.hashCode();

        // toString
        StringBuilder sb = new StringBuilder(128);
        sb.append("TorrentMetaInfo{{ ");

        sb
            .append("CreationDate(").append(this.creationDate).append("), ").append("Comment(\"").append(this.comment)
            .append("\"), ").append("Announce(").append(this.announce).append("), ").append("AnnounceList(").append(
                this.announceList).append("), ").append("Info(").append(this.info).append("), ").append(" }}");

        chachedToString = sb.toString();
    }