0001: //bookmarksDB.java
0002: //-------------------------------------
0003: //part of YACY
0004: //(C) by Michael Peter Christen; mc@anomic.de
0005: //first published on http://www.anomic.de
0006: //Frankfurt, Germany, 2004
0007: //
0008: //This file has been originally contributed by Alexander Schier
0009: //
0010: //This program is free software; you can redistribute it and/or modify
0011: //it under the terms of the GNU General Public License as published by
0012: //the Free Software Foundation; either version 2 of the License, or
0013: //(at your option) any later version.
0014: //
0015: //This program is distributed in the hope that it will be useful,
0016: //but WITHOUT ANY WARRANTY; without even the implied warranty of
0017: //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0018: //GNU General Public License for more details.
0019: //
0020: //You should have received a copy of the GNU General Public License
0021: //along with this program; if not, write to the Free Software
0022: //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0023: //
0024: //Using this software in any meaning (reading, learning, copying, compiling,
0025: //running) means that you agree that the Author(s) is (are) not responsible
0026: //for cost, loss of data or any harm that may be caused directly or indirectly
0027: //by usage of this softare or this documentation. The usage of this software
0028: //is on your own risk. The installation and usage (starting/running) of this
0029: //software may allow other people or application to access your computer and
0030: //any attached devices and is highly dependent on the configuration of the
0031: //software which must be done by the user of the software; the author(s) is
0032: //(are) also not responsible for proper configuration and usage of the
0033: //software, even if provoked by documentation provided together with
0034: //the software.
0035: //
0036: //Any changes to this file according to the GPL as documented in the file
0037: //gpl.txt aside this file in the shipment you received can be done to the
0038: //lines that follows this copyright notice here, but changes must not be
0039: //done inside the copyright notive above. A re-distribution must contain
0040: //the intact and unchanged copyright notice.
0041: //Contributions and changes to the program code must be marked as such.
0042: package de.anomic.data;
0043:
0044: import java.io.ByteArrayInputStream;
0045: import java.io.File;
0046: import java.io.IOException;
0047: import java.io.InputStream;
0048: import java.io.InputStreamReader;
0049: import java.io.UnsupportedEncodingException;
0050: import java.io.Writer;
0051: import java.net.MalformedURLException;
0052: import java.text.ParseException;
0053: import java.text.SimpleDateFormat;
0054: import java.util.ArrayList;
0055: import java.util.Comparator;
0056: import java.util.Date;
0057: import java.util.HashMap;
0058: import java.util.HashSet;
0059: import java.util.Iterator;
0060: import java.util.Map;
0061: import java.util.Set;
0062: import java.util.TreeSet;
0063: import java.util.regex.Pattern;
0064:
0065: import javax.xml.parsers.DocumentBuilder;
0066: import javax.xml.parsers.DocumentBuilderFactory;
0067: import javax.xml.parsers.ParserConfigurationException;
0068:
0069: import org.w3c.dom.Document;
0070: import org.w3c.dom.NamedNodeMap;
0071: import org.w3c.dom.Node;
0072: import org.w3c.dom.NodeList;
0073: import org.xml.sax.SAXException;
0074:
0075: import de.anomic.htmlFilter.htmlFilterContentScraper;
0076: import de.anomic.htmlFilter.htmlFilterWriter;
0077: import de.anomic.kelondro.kelondroCloneableIterator;
0078: import de.anomic.kelondro.kelondroDyn;
0079: import de.anomic.kelondro.kelondroException;
0080: import de.anomic.kelondro.kelondroMapObjects;
0081: import de.anomic.kelondro.kelondroNaturalOrder;
0082: import de.anomic.kelondro.kelondroObjects;
0083: import de.anomic.kelondro.kelondroObjectsMapEntry;
0084: import de.anomic.plasma.plasmaCondenser;
0085: import de.anomic.server.serverDate;
0086: import de.anomic.server.serverFileUtils;
0087: import de.anomic.server.logging.serverLog;
0088: import de.anomic.yacy.yacyURL;
0089:
0090: public class bookmarksDB {
0091: // ------------------------------------
0092: // Declaration of Class-Attributes
0093: // ------------------------------------
0094:
0095: final static int SORT_ALPHA = 1;
0096: final static int SORT_SIZE = 2;
0097: final static int SHOW_ALL = -1;
0098:
0099: // bookmarks
0100: kelondroObjects bookmarksTable; // kelondroMap bookmarksTable;
0101:
0102: // tags
0103: kelondroMapObjects tagsTable;
0104: HashMap<String, Tag> tagCache;
0105:
0106: // dates
0107: kelondroMapObjects datesTable;
0108:
0109: // ------------------------------------
0110: // bookmarksDB's class constructor
0111: // ------------------------------------
0112:
0113: public bookmarksDB(File bookmarksFile, File tagsFile,
0114: File datesFile, long preloadTime) {
0115: // bookmarks
0116: tagCache = new HashMap<String, Tag>();
0117: bookmarksFile.getParentFile().mkdirs();
0118: //this.bookmarksTable = new kelondroMap(kelondroDyn.open(bookmarksFile, bufferkb * 1024, preloadTime, 12, 256, '_', true, false));
0119: this .bookmarksTable = new kelondroObjects(new kelondroDyn(
0120: bookmarksFile, true, true, preloadTime, 12, 256, '_',
0121: kelondroNaturalOrder.naturalOrder, true, false, false),
0122: 1000);
0123:
0124: // tags
0125: tagsFile.getParentFile().mkdirs();
0126: boolean tagsFileExisted = tagsFile.exists();
0127: this .tagsTable = new kelondroMapObjects(new kelondroDyn(
0128: tagsFile, true, true, preloadTime, 12, 256, '_',
0129: kelondroNaturalOrder.naturalOrder, true, false, false),
0130: 500);
0131: if (!tagsFileExisted)
0132: rebuildTags();
0133:
0134: // dates
0135: boolean datesExisted = datesFile.exists();
0136: this .datesTable = new kelondroMapObjects(new kelondroDyn(
0137: datesFile, true, true, preloadTime, 20, 256, '_',
0138: kelondroNaturalOrder.naturalOrder, true, false, false),
0139: 500);
0140: if (!datesExisted)
0141: rebuildDates();
0142:
0143: }
0144:
0145: // -----------------------------------------------------
0146: // bookmarksDB's functions for 'destructing' the class
0147: // -----------------------------------------------------
0148:
0149: public void close() {
0150: bookmarksTable.close();
0151: flushTagCache();
0152: tagsTable.close();
0153: datesTable.close();
0154: }
0155:
0156: // -------------------------------------
0157: // bookmarksDB's public helper functions
0158: // -------------------------------------
0159:
0160: /**
0161: * returns an object of type String that contains a tagHash
0162: * @param tagName an object of type String with the name of the tag.
0163: * tagName is converted to lower case before hash is generated!
0164: */
0165: public static String tagHash(String tagName) {
0166: return plasmaCondenser.word2hash(tagName.toLowerCase());
0167: }
0168:
0169: public static String tagHash(String tagName, String user) {
0170: return plasmaCondenser.word2hash(user + ":"
0171: + tagName.toLowerCase());
0172: }
0173:
0174: public Iterator<String> getFolderList(boolean priv) {
0175:
0176: Set<String> folders = new TreeSet<String>();
0177: String path = "";
0178: Iterator<Tag> it = this .getTagIterator(priv);
0179: Tag tag;
0180:
0181: while (it.hasNext()) {
0182: tag = it.next();
0183: if (tag.getFriendlyName().startsWith("/")) {
0184: path = tag.getFriendlyName();
0185: path = cleanTagsString(path);
0186: while (path.length() > 0) {
0187: folders.add(path);
0188: path = path.replaceAll("(/.[^/]*$)", ""); // create missing folders in path
0189: }
0190: }
0191: }
0192: folders.add("\uffff");
0193: return (Iterator<String>) folders.iterator();
0194: }
0195:
0196: public static String cleanTagsString(String tagsString) {
0197:
0198: // get rid of heading, trailing and double commas since they are useless
0199: while (tagsString.startsWith(",")) {
0200: tagsString = tagsString.substring(1);
0201: }
0202: while (tagsString.endsWith(",")) {
0203: tagsString = tagsString.substring(0,
0204: tagsString.length() - 1);
0205: }
0206: while (tagsString.contains(",,")) {
0207: tagsString = tagsString.replaceAll(",,", ",");
0208: }
0209: // get rid of double and trailing slashes
0210: while (tagsString.endsWith("/")) {
0211: tagsString = tagsString.substring(0,
0212: tagsString.length() - 1);
0213: }
0214: while (tagsString.contains("/,")) {
0215: tagsString = tagsString.replaceAll("/,", ",");
0216: }
0217: while (tagsString.contains("//")) {
0218: tagsString = tagsString.replaceAll("//", "/");
0219: }
0220: // space characters following a comma are removed
0221: tagsString = tagsString.replaceAll(",\\s+", ",");
0222:
0223: return tagsString;
0224: }
0225:
0226: // -----------------------------------------------------------
0227: // bookmarksDB's functions for bookmarksTable / bookmarkCache
0228: // -----------------------------------------------------------
0229:
0230: public Bookmark createBookmark(String url, String user) {
0231: if (url == null || url.length() == 0)
0232: return null;
0233: Bookmark bk = new Bookmark(url);
0234: bk.setOwner(user);
0235: return (bk.getUrlHash() == null || bk.toMap() == null) ? null
0236: : bk;
0237: }
0238:
0239: // returning the number of bookmarks
0240: public int bookmarksSize() {
0241: return bookmarksTable.size();
0242: }
0243:
0244: // adding a bookmark to the bookmarksDB
0245: public void saveBookmark(Bookmark bookmark) {
0246: try {
0247: bookmarksTable.set(bookmark.getUrlHash(), bookmark);
0248: } catch (IOException e) {
0249: // TODO Auto-generated catch block
0250: e.printStackTrace();
0251: }
0252: }
0253:
0254: public String addBookmark(Bookmark bookmark) {
0255: saveBookmark(bookmark);
0256: return bookmark.getUrlHash();
0257:
0258: }
0259:
0260: public Bookmark getBookmark(String urlHash) {
0261: try {
0262: kelondroObjectsMapEntry map = (kelondroObjectsMapEntry) bookmarksTable
0263: .get(urlHash);
0264: if (map == null)
0265: return null;
0266: if (map instanceof Bookmark)
0267: return (Bookmark) map;
0268: return new Bookmark(map);
0269: } catch (IOException e) {
0270: return null;
0271: }
0272: }
0273:
0274: public boolean removeBookmark(String urlHash) {
0275: Bookmark bookmark = getBookmark(urlHash);
0276: if (bookmark == null)
0277: return false; //does not exist
0278: Set<String> tags = bookmark.getTags();
0279: bookmarksDB.Tag tag = null;
0280: Iterator<String> it = tags.iterator();
0281: while (it.hasNext()) {
0282: tag = getTag(tagHash(it.next()));
0283: if (tag != null) {
0284: tag.delete(urlHash);
0285: saveTag(tag);
0286: }
0287: }
0288: Bookmark b;
0289: try {
0290: b = getBookmark(urlHash);
0291: bookmarksTable.remove(urlHash);
0292: } catch (IOException e) {
0293: b = null;
0294: }
0295: return b != null;
0296: }
0297:
0298: public Iterator<Bookmark> bookmarkIterator(boolean up) {
0299: try {
0300: return new bookmarkIterator(up);
0301: } catch (IOException e) {
0302: return new HashSet<Bookmark>().iterator();
0303: }
0304: }
0305:
0306: public Iterator<String> getBookmarksIterator(boolean priv) {
0307: TreeSet<String> set = new TreeSet<String>(
0308: new bookmarkComparator(true));
0309: Iterator<Bookmark> it = bookmarkIterator(true);
0310: Bookmark bm;
0311: while (it.hasNext()) {
0312: bm = (Bookmark) it.next();
0313: if (priv || bm.getPublic()) {
0314: set.add(bm.getUrlHash());
0315: }
0316: }
0317: return set.iterator();
0318: }
0319:
0320: public Iterator<String> getBookmarksIterator(String tagName,
0321: boolean priv) {
0322: TreeSet<String> set = new TreeSet<String>(
0323: new bookmarkComparator(true));
0324: String tagHash = tagHash(tagName);
0325: Tag tag = getTag(tagHash);
0326: Set<String> hashes = new HashSet<String>();
0327: if (tag != null) {
0328: hashes = getTag(tagHash).getUrlHashes();
0329: }
0330: if (priv) {
0331: set.addAll(hashes);
0332: } else {
0333: Iterator<String> it = hashes.iterator();
0334: Bookmark bm;
0335: while (it.hasNext()) {
0336: bm = getBookmark(it.next());
0337: if (bm.getPublic()) {
0338: set.add(bm.getUrlHash());
0339: }
0340: }
0341: }
0342: return set.iterator();
0343: }
0344:
0345: // -------------------------------------------------
0346: // bookmarksDB's functions for tagsTable / tagCache
0347: // -------------------------------------------------
0348:
0349: // returning the number of tags
0350: public int tagsSize() { // TODO: shouldn't this be public int tagSize()
0351: return tagSize(false);
0352: }
0353:
0354: public int tagSize(boolean flushed) {
0355: if (flushed)
0356: flushTagCache();
0357: return tagsTable.size();
0358: }
0359:
0360: /**
0361: * load/retrieve an object of type Tag from the tagsTable (also save it in tagCache)
0362: * @param hash an object of type String, containing a tagHash
0363: */
0364: public Tag loadTag(String hash) { // TODO: check if loadTag() could be private
0365: HashMap<String, String> map;
0366: Tag ret = null;
0367: map = tagsTable.getMap(hash);
0368: if (map != null) {
0369: ret = new Tag(hash, map);
0370: tagCache.put(hash, ret);
0371: }
0372: return ret;
0373: }
0374:
0375: /**
0376: * retrieve an object of type Tag from the the tagCache, if object is not cached return loadTag(hash)
0377: * @param hash an object of type String, containing a tagHash
0378: */
0379: public Tag getTag(String hash) {
0380: if (tagCache.containsKey(hash)) {
0381: return tagCache.get(hash);
0382: }
0383: return loadTag(hash); //null if it does not exists
0384: }
0385:
0386: /**
0387: * store a Tag in tagsTable or remove an empty tag
0388: * @param tag an object of type Tag to be stored/removed
0389: */
0390: public void storeTag(Tag tag) {
0391: try {
0392: if (tag.size() > 0) {
0393: bookmarksDB.this .tagsTable.set(tag.getTagHash(), tag
0394: .getMap());
0395: } else {
0396: bookmarksDB.this .tagsTable.remove(tag.getTagHash());
0397: }
0398: } catch (IOException e) {
0399: }
0400: }
0401:
0402: /**
0403: * save a Tag in tagCache; see also flushTagCache(), addTag(), loadTag()
0404: * @param tag an object of type Tag to be saved in tagCache
0405: */
0406: public void saveTag(Tag tag) {
0407: if (tag != null) {
0408: tagCache.put(tag.getTagHash(), tag);
0409: }
0410: }
0411:
0412: public void flushTagCache() {
0413: Iterator<String> it = tagCache.keySet().iterator();
0414: while (it.hasNext()) {
0415: storeTag(tagCache.get(it.next()));
0416: }
0417: tagCache = new HashMap<String, Tag>();
0418: }
0419:
0420: public String addTag(Tag tag) { // TODO: is addTag() really needed - check storeTag() and saveTag()
0421: //tagsTable.set(tag.getTagName(), tag.getMap());
0422: //tagCache.put(tag.getTagHash(), tag);
0423: saveTag(tag);
0424: return tag.getTagName();
0425: }
0426:
0427: public void removeTag(String hash) {
0428: try {
0429: if (tagCache.containsKey(hash)) {
0430: tagCache.remove(hash);
0431: }
0432: tagsTable.remove(hash);
0433: } catch (IOException e) {
0434: }
0435: }
0436:
0437: public Iterator<Tag> tagIterator(boolean up) {
0438: try {
0439: return new tagIterator(up);
0440: } catch (IOException e) {
0441: return new HashSet<Tag>().iterator();
0442: }
0443: }
0444:
0445: public Iterator<Tag> getTagIterator(boolean priv) {
0446: return getTagIterator(priv, 1);
0447: }
0448:
0449: public Iterator<Tag> getTagIterator(boolean priv, int c) {
0450: Comparator<Tag> comp;
0451: if (c == SORT_SIZE)
0452: comp = new tagSizeComparator();
0453: else
0454: comp = new tagComparator();
0455: TreeSet<Tag> set = new TreeSet<Tag>(comp);
0456: Iterator<Tag> it = tagIterator(true);
0457: Tag tag;
0458: while (it.hasNext()) {
0459: tag = (Tag) it.next();
0460: if (priv || tag.hasPublicItems()) {
0461: set.add(tag);
0462: }
0463: }
0464: return set.iterator();
0465: }
0466:
0467: public Iterator<Tag> getTagIterator(boolean priv, int comp, int max) {
0468: if (max == SHOW_ALL)
0469: return getTagIterator(priv, comp);
0470: Iterator<Tag> it = getTagIterator(priv, comp);
0471: TreeSet<Tag> set = new TreeSet<Tag>(new tagComparator());
0472: int count = 0;
0473: while (it.hasNext() && count <= max) {
0474: set.add(it.next());
0475: count++;
0476: }
0477: return set.iterator();
0478: }
0479:
0480: public Iterator<Tag> getTagIterator(String tagName, boolean priv) {
0481: return getTagIterator(tagName, priv, 1);
0482: }
0483:
0484: public Iterator<Tag> getTagIterator(String tagName, boolean priv,
0485: int comp) {
0486: Comparator<Tag> c;
0487: if (comp == SORT_SIZE)
0488: c = new tagSizeComparator();
0489: else
0490: c = new tagComparator();
0491: TreeSet<Tag> set = new TreeSet<Tag>(c);
0492: Iterator<String> it = null;
0493: Iterator<String> bit = getBookmarksIterator(tagName, priv);
0494: Bookmark bm;
0495: Tag tag;
0496: Set<String> tags;
0497: while (bit.hasNext()) {
0498: bm = getBookmark(bit.next());
0499: tags = bm.getTags();
0500: it = tags.iterator();
0501: while (it.hasNext()) {
0502: tag = getTag(tagHash(it.next()));
0503: if (priv || tag.hasPublicItems()) {
0504: set.add(tag);
0505: }
0506: }
0507: }
0508: return set.iterator();
0509: }
0510:
0511: public Iterator<Tag> getTagIterator(String tagName, boolean priv,
0512: int comp, int max) {
0513: if (max == SHOW_ALL)
0514: return getTagIterator(priv, comp);
0515: Iterator<Tag> it = getTagIterator(tagName, priv, comp);
0516: TreeSet<Tag> set = new TreeSet<Tag>(new tagComparator());
0517: int count = 0;
0518: while (it.hasNext() && count <= max) {
0519: set.add(it.next());
0520: count++;
0521: }
0522: return set.iterator();
0523: }
0524:
0525: // rebuilds the tagsDB from the bookmarksDB
0526: public void rebuildTags() {
0527: serverLog.logInfo("BOOKMARKS",
0528: "rebuilding tags.db from bookmarks.db...");
0529: Iterator<Bookmark> it = bookmarkIterator(true);
0530: Bookmark bookmark;
0531: Tag tag;
0532: String[] tags;
0533: while (it.hasNext()) {
0534: bookmark = (Bookmark) it.next();
0535: tags = bookmark.getTagsString().split(",");
0536: tag = null;
0537: for (int i = 0; i < tags.length; i++) {
0538: tag = getTag(tagHash(tags[i]));
0539: if (tag == null) {
0540: tag = new Tag(tags[i]);
0541: }
0542: tag.addUrl(bookmark.getUrlHash());
0543: saveTag(tag);
0544: }
0545: }
0546: flushTagCache();
0547: serverLog.logInfo("BOOKMARKS", "Rebuilt " + tagsTable.size()
0548: + " tags using your " + bookmarksTable.size()
0549: + " bookmarks.");
0550: }
0551:
0552: // ---------------------------------------
0553: // bookmarksDB's functions for datesTable
0554: // ---------------------------------------
0555:
0556: public bookmarksDate getDate(String date) {
0557: HashMap<String, String> map;
0558: map = datesTable.getMap(date);
0559: if (map == null)
0560: return new bookmarksDate(date);
0561: return new bookmarksDate(date, map);
0562: }
0563:
0564: // rebuilds the datesDB from the bookmarksDB
0565: public void rebuildDates() {
0566: serverLog.logInfo("BOOKMARKS",
0567: "rebuilding dates.db from bookmarks.db...");
0568: Iterator<Bookmark> it = bookmarkIterator(true);
0569: Bookmark bookmark;
0570: String date;
0571: bookmarksDate bmDate;
0572: while (it.hasNext()) {
0573: bookmark = (Bookmark) it.next();
0574: date = (new SimpleDateFormat("yyyy-MM-dd"))
0575: .format(new Date(bookmark.getTimeStamp()));
0576: bmDate = getDate(date);
0577: if (bmDate == null) {
0578: bmDate = new bookmarksDate(date);
0579: }
0580: bmDate.add(bookmark.getUrlHash());
0581: bmDate.setDatesTable();
0582: }
0583: serverLog.logInfo("BOOKMARKS", "Rebuilt " + datesTable.size()
0584: + " dates using your " + bookmarksTable.size()
0585: + " bookmarks.");
0586: }
0587:
0588: // -------------------------------------
0589: // bookmarksDB's experimental functions
0590: // -------------------------------------
0591:
0592: public boolean renameTag(String oldName, String newName) {
0593:
0594: String oldHash = tagHash(oldName);
0595: //String newHash=tagHash(newName);
0596: Tag tag = getTag(oldHash); // set tag to oldHash
0597: if (tag != null) {
0598: Set<String> urlHashes = tag.getUrlHashes(); // preserve urlHashes of tag
0599: removeTag(oldHash);
0600: Iterator<String> it = urlHashes.iterator();
0601: Bookmark bookmark;
0602: Set<String> tags = new HashSet<String>();
0603: String tagsString;
0604: while (it.hasNext()) { // looping through all bookmarks which were tagged with oldName
0605: bookmark = getBookmark((String) it.next());
0606: tagsString = bookmark.getTagsString();
0607: // Set<String> tags is difficult with case sensitivity, so I tried
0608: // Set<String> tags = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER), but it didn't do the trick :-(
0609: // so I chose the tagsString and replaceAll() as workaround
0610: // unfortunately doing the replaceAll with Patterns (regexp) really costs performance
0611: tags = listManager.string2set(Pattern.compile(oldName,
0612: 66).matcher(tagsString).replaceAll(newName)); // TODO: need better solution for renaming tags
0613: bookmark.setTags(tags, true); // I had to adjust setTags() for this to work
0614: saveBookmark(bookmark);
0615: }
0616: return true;
0617: }
0618: return false;
0619: }
0620:
0621: // --------------------------------------
0622: // bookmarksDB's Import/Export functions
0623: // --------------------------------------
0624:
0625: public int importFromBookmarks(yacyURL baseURL, String input,
0626: String tag, boolean importPublic) {
0627: try {
0628: // convert string to input stream
0629: ByteArrayInputStream byteIn = new ByteArrayInputStream(
0630: input.getBytes("UTF-8"));
0631: InputStreamReader reader = new InputStreamReader(byteIn,
0632: "UTF-8");
0633:
0634: // import stream
0635: return this .importFromBookmarks(baseURL, reader, tag,
0636: importPublic);
0637: } catch (UnsupportedEncodingException e) {
0638: return 0;
0639: }
0640: }
0641:
0642: public int importFromBookmarks(yacyURL baseURL,
0643: InputStreamReader input, String tag, boolean importPublic) {
0644:
0645: int importCount = 0;
0646:
0647: Map<yacyURL, String> links = new HashMap<yacyURL, String>();
0648: String title;
0649: yacyURL url;
0650: Bookmark bm;
0651: Set<String> tags = listManager.string2set(tag); //this allow multiple default tags
0652: try {
0653: //load the links
0654: htmlFilterContentScraper scraper = new htmlFilterContentScraper(
0655: baseURL);
0656: //OutputStream os = new htmlFilterOutputStream(null, scraper, null, false);
0657: Writer writer = new htmlFilterWriter(null, null, scraper,
0658: null, false);
0659: serverFileUtils.copy(input, writer);
0660: writer.close();
0661: links = scraper.getAnchors();
0662: } catch (IOException e) {
0663: }
0664: Iterator<yacyURL> it = links.keySet().iterator();
0665: while (it.hasNext()) {
0666: url = it.next();
0667: title = (String) links.get(url);
0668: serverLog.logInfo("BOOKMARKS", "links.get(url)");
0669: if (title.equals("")) {//cannot be displayed
0670: title = url.toString();
0671: }
0672: bm = new Bookmark(url.toString());
0673: bm.setProperty(Bookmark.BOOKMARK_TITLE, title);
0674: bm.setTags(tags);
0675: bm.setPublic(importPublic);
0676: saveBookmark(bm);
0677:
0678: importCount++;
0679: }
0680:
0681: flushTagCache();
0682:
0683: return importCount;
0684: }
0685:
0686: public int importFromXML(String input, boolean importPublic) {
0687: try {
0688: // convert string to input stream
0689: ByteArrayInputStream byteIn = new ByteArrayInputStream(
0690: input.getBytes("UTF-8"));
0691:
0692: // import stream
0693: return this .importFromXML(byteIn, importPublic);
0694: } catch (UnsupportedEncodingException e) {
0695: return 0;
0696: }
0697: }
0698:
0699: public int importFromXML(InputStream input, boolean importPublic) {
0700: DocumentBuilderFactory factory = DocumentBuilderFactory
0701: .newInstance();
0702: factory.setValidating(false);
0703: factory.setNamespaceAware(false);
0704: DocumentBuilder builder;
0705: try {
0706: builder = factory.newDocumentBuilder();
0707: Document doc = builder.parse(input);
0708: return parseXMLimport(doc, importPublic);
0709: } catch (ParserConfigurationException e) {
0710: } catch (SAXException e) {
0711: } catch (IOException e) {
0712: }
0713: return 0;
0714:
0715: }
0716:
0717: public int parseXMLimport(Node doc, boolean importPublic) {
0718: int importCount = 0;
0719: if (doc.getNodeName() == "post") {
0720: NamedNodeMap attributes = doc.getAttributes();
0721: String url = attributes.getNamedItem("href").getNodeValue();
0722: if (url.equals("")) {
0723: return 0;
0724: }
0725: Bookmark bm = new Bookmark(url);
0726: String tagsString = "";
0727: String title = "";
0728: String description = "";
0729: String time = "";
0730: if (attributes.getNamedItem("tag") != null) {
0731: tagsString = attributes.getNamedItem("tag")
0732: .getNodeValue();
0733: }
0734: if (attributes.getNamedItem("description") != null) {
0735: title = attributes.getNamedItem("description")
0736: .getNodeValue();
0737: }
0738: if (attributes.getNamedItem("extended") != null) {
0739: description = attributes.getNamedItem("extended")
0740: .getNodeValue();
0741: }
0742: if (attributes.getNamedItem("time") != null) {
0743: time = attributes.getNamedItem("time").getNodeValue();
0744: }
0745: Set<String> tags = new HashSet<String>();
0746:
0747: if (title != null) {
0748: bm.setProperty(Bookmark.BOOKMARK_TITLE, title);
0749: }
0750: if (tagsString != null) {
0751: tags = listManager.string2set(tagsString.replace(' ',
0752: ','));
0753: }
0754: bm.setTags(tags, true);
0755: if (time != null) {
0756:
0757: Date parsedDate = null;
0758: try {
0759: parsedDate = serverDate.parseISO8601(time);
0760: } catch (ParseException e) {
0761: parsedDate = new Date();
0762: }
0763: bm.setTimeStamp(parsedDate.getTime());
0764: }
0765: if (description != null) {
0766: bm.setProperty(Bookmark.BOOKMARK_DESCRIPTION,
0767: description);
0768: }
0769: bm.setPublic(importPublic);
0770: saveBookmark(bm);
0771:
0772: importCount++;
0773: }
0774: NodeList children = doc.getChildNodes();
0775: if (children != null) {
0776: for (int i = 0; i < children.getLength(); i++) {
0777: importCount += parseXMLimport(children.item(i),
0778: importPublic);
0779: }
0780: }
0781: flushTagCache();
0782:
0783: return importCount;
0784: }
0785:
0786: // --------------------------------------
0787: // bookmarksDB's Subclasses
0788: // --------------------------------------
0789:
0790: /**
0791: * Subclass of bookmarksDB, which provides the Tag object-type
0792: */
0793: public class Tag {
0794: public static final String URL_HASHES = "urlHashes";
0795: public static final String TAG_NAME = "tagName";
0796: private String tagHash;
0797: private HashMap<String, String> mem;
0798: private Set<String> urlHashes;
0799:
0800: public Tag(String hash, HashMap<String, String> map) {
0801: tagHash = hash;
0802: mem = map;
0803: if (mem.containsKey(URL_HASHES))
0804: urlHashes = listManager.string2set(mem.get(URL_HASHES));
0805: else
0806: urlHashes = new HashSet<String>();
0807: }
0808:
0809: public Tag(String name, HashSet<String> entries) {
0810: tagHash = tagHash(name);
0811: mem = new HashMap<String, String>();
0812: //mem.put(URL_HASHES, listManager.arraylist2string(entries));
0813: urlHashes = entries;
0814: mem.put(TAG_NAME, name);
0815: }
0816:
0817: public Tag(String name) {
0818: tagHash = tagHash(name);
0819: mem = new HashMap<String, String>();
0820: //mem.put(URL_HASHES, "");
0821: urlHashes = new HashSet<String>();
0822: mem.put(TAG_NAME, name);
0823: }
0824:
0825: public HashMap<String, String> getMap() {
0826: mem.put(URL_HASHES, listManager
0827: .collection2string(this .urlHashes));
0828: return mem;
0829: }
0830:
0831: /**
0832: * get the lowercase Tagname
0833: */
0834: public String getTagName() {
0835: /*if(this.mem.containsKey(TAG_NAME)){
0836: return (String) this.mem.get(TAG_NAME);
0837: }
0838: return "";*/
0839: return getFriendlyName().toLowerCase();
0840: }
0841:
0842: public String getTagHash() {
0843: return tagHash;
0844: }
0845:
0846: /**
0847: * @return the tag name, with all uppercase chars
0848: */
0849: public String getFriendlyName() {
0850: /*if(this.mem.containsKey(TAG_FRIENDLY_NAME)){
0851: return (String) this.mem.get(TAG_FRIENDLY_NAME);
0852: }
0853: return getTagName();*/
0854: if (this .mem.containsKey(TAG_NAME)) {
0855: return this .mem.get(TAG_NAME);
0856: }
0857: return "notagname";
0858: }
0859:
0860: public Set<String> getUrlHashes() {
0861: return urlHashes;
0862: }
0863:
0864: public boolean hasPublicItems() {
0865: Iterator<String> it = getBookmarksIterator(this
0866: .getTagHash(), false);
0867: if (it.hasNext()) {
0868: return true;
0869: }
0870: return false;
0871: }
0872:
0873: public void addUrl(String urlHash) {
0874: urlHashes.add(urlHash);
0875: }
0876:
0877: public void delete(String urlHash) {
0878: urlHashes.remove(urlHash);
0879: }
0880:
0881: public int size() {
0882: return urlHashes.size();
0883: }
0884: }
0885:
0886: /**
0887: * Subclass of bookmarksDB, which provide the bookmarksDate object-type
0888: */
0889: public class bookmarksDate {
0890: public static final String URL_HASHES = "urlHashes";
0891: private HashMap<String, String> mem;
0892: String date;
0893:
0894: public bookmarksDate(String mydate) {
0895: //round to seconds, but store as milliseconds (java timestamp)
0896: date = String
0897: .valueOf((Long.parseLong(mydate) / 1000) * 1000);
0898: mem = new HashMap<String, String>();
0899: mem.put(URL_HASHES, "");
0900: }
0901:
0902: public bookmarksDate(String mydate, HashMap<String, String> map) {
0903: //round to seconds, but store as milliseconds (java timestamp)
0904: date = String
0905: .valueOf((Long.parseLong(mydate) / 1000) * 1000);
0906: mem = map;
0907: }
0908:
0909: public bookmarksDate(String mydate, ArrayList<String> entries) {
0910: //round to seconds, but store as milliseconds (java timestamp)
0911: date = String
0912: .valueOf((Long.parseLong(mydate) / 1000) * 1000);
0913: mem = new HashMap<String, String>();
0914: mem.put(URL_HASHES, listManager.collection2string(entries));
0915: }
0916:
0917: public void add(String urlHash) {
0918: String urlHashes = mem.get(URL_HASHES);
0919: ArrayList<String> list;
0920: if (urlHashes != null && !urlHashes.equals("")) {
0921: list = listManager.string2arraylist(urlHashes);
0922: } else {
0923: list = new ArrayList<String>();
0924: }
0925: if (!list.contains(urlHash) && urlHash != null
0926: && !urlHash.equals("")) {
0927: list.add(urlHash);
0928: }
0929: this .mem.put(URL_HASHES, listManager
0930: .collection2string(list));
0931: /*if(urlHashes!=null && !urlHashes.equals("") ){
0932: if(urlHashes.indexOf(urlHash) <0){
0933: this.mem.put(URL_HASHES, urlHashes+","+urlHash);
0934: }
0935: }else{
0936: this.mem.put(URL_HASHES, urlHash);
0937: }*/
0938: }
0939:
0940: public void delete(String urlHash) {
0941: ArrayList<String> list = listManager
0942: .string2arraylist(this .mem.get(URL_HASHES));
0943: if (list.contains(urlHash)) {
0944: list.remove(urlHash);
0945: }
0946: this .mem.put(URL_HASHES, listManager
0947: .collection2string(list));
0948: }
0949:
0950: public void setDatesTable() {
0951: try {
0952: if (this .size() > 0) {
0953: bookmarksDB.this .datesTable.set(getDateString(),
0954: mem);
0955: } else {
0956: bookmarksDB.this .datesTable.remove(getDateString());
0957: }
0958: } catch (IOException e) {
0959: }
0960: }
0961:
0962: public String getDateString() {
0963: return date;
0964: }
0965:
0966: public ArrayList<String> getBookmarkList() {
0967: return listManager.string2arraylist(this .mem
0968: .get(URL_HASHES));
0969: }
0970:
0971: public int size() {
0972: return listManager.string2arraylist(
0973: this .mem.get(URL_HASHES)).size();
0974: }
0975: }
0976:
0977: /**
0978: * Subclass of bookmarksDB, which provides the Bookmark object-type
0979: */
0980: public class Bookmark extends kelondroObjectsMapEntry {
0981: public static final String BOOKMARK_URL = "bookmarkUrl";
0982: public static final String BOOKMARK_TITLE = "bookmarkTitle";
0983: public static final String BOOKMARK_DESCRIPTION = "bookmarkDesc";
0984: public static final String BOOKMARK_TAGS = "bookmarkTags";
0985: public static final String BOOKMARK_PUBLIC = "bookmarkPublic";
0986: public static final String BOOKMARK_TIMESTAMP = "bookmarkTimestamp";
0987: public static final String BOOKMARK_OWNER = "bookmarkOwner";
0988: public static final String BOOKMARK_IS_FEED = "bookmarkIsFeed";
0989: private String urlHash;
0990: private Set<String> tags;
0991: private long timestamp;
0992:
0993: public Bookmark(String urlHash, HashMap<String, String> map) {
0994: super (map);
0995: this .urlHash = urlHash;
0996: if (map.containsKey(BOOKMARK_TAGS))
0997: tags = listManager.string2set((String) map
0998: .get(BOOKMARK_TAGS));
0999: else
1000: tags = new HashSet<String>();
1001: loadTimestamp();
1002: }
1003:
1004: public Bookmark(String url) {
1005: super ();
1006: if (!url.toLowerCase().startsWith("http://")
1007: && !url.toLowerCase().startsWith("https://")) {
1008: url = "http://" + url;
1009: }
1010: try {
1011: this .urlHash = (new yacyURL(url, null)).hash();
1012: } catch (MalformedURLException e) {
1013: this .urlHash = null;
1014: }
1015: entry.put(BOOKMARK_URL, url);
1016: this .timestamp = System.currentTimeMillis();
1017: tags = new HashSet<String>();
1018: Bookmark oldBm = getBookmark(this .urlHash);
1019: if (oldBm != null
1020: && oldBm.entry.containsKey(BOOKMARK_TIMESTAMP)) {
1021: entry.put(BOOKMARK_TIMESTAMP, oldBm.entry
1022: .get(BOOKMARK_TIMESTAMP)); //preserve timestamp on edit
1023: } else {
1024: entry.put(BOOKMARK_TIMESTAMP, String.valueOf(System
1025: .currentTimeMillis()));
1026: }
1027: bookmarksDate bmDate = getDate(entry
1028: .get(BOOKMARK_TIMESTAMP));
1029: bmDate.add(this .urlHash);
1030: bmDate.setDatesTable();
1031:
1032: removeBookmark(this .urlHash); //prevent empty tags
1033: }
1034:
1035: public Bookmark(String urlHash, yacyURL url) {
1036: super ();
1037: this .urlHash = urlHash;
1038: entry.put(BOOKMARK_URL, url.toNormalform(false, true));
1039: tags = new HashSet<String>();
1040: timestamp = System.currentTimeMillis();
1041: }
1042:
1043: public Bookmark(String urlHash, String url) {
1044: super ();
1045: this .urlHash = urlHash;
1046: entry.put(BOOKMARK_URL, url);
1047: tags = new HashSet<String>();
1048: timestamp = System.currentTimeMillis();
1049: }
1050:
1051: public Bookmark(kelondroObjectsMapEntry map)
1052: throws MalformedURLException {
1053: this ((new yacyURL((String) map.map().get(BOOKMARK_URL),
1054: null)).hash(), map.map());
1055: }
1056:
1057: private Map<String, String> toMap() {
1058: entry.put(BOOKMARK_TAGS, listManager
1059: .collection2string(tags));
1060: entry.put(BOOKMARK_TIMESTAMP, String
1061: .valueOf(this .timestamp));
1062: return entry;
1063: }
1064:
1065: private void loadTimestamp() {
1066: if (entry.containsKey(BOOKMARK_TIMESTAMP))
1067: this .timestamp = Long.parseLong(entry
1068: .get(BOOKMARK_TIMESTAMP));
1069: }
1070:
1071: public String getUrlHash() {
1072: return urlHash;
1073: }
1074:
1075: public String getUrl() {
1076: return entry.get(BOOKMARK_URL);
1077: }
1078:
1079: public Set<String> getTags() {
1080: return tags;
1081: }
1082:
1083: public String getTagsString() {
1084: String s[] = listManager.collection2string(getTags())
1085: .split(",");
1086: String tagsString = "";
1087: for (int i = 0; i < s.length; i++) {
1088: if (!s[i].startsWith("/")) {
1089: tagsString += s[i] + ",";
1090: }
1091: }
1092: return tagsString;
1093: }
1094:
1095: public String getFoldersString() {
1096: String s[] = listManager.collection2string(getTags())
1097: .split(",");
1098: String foldersString = "";
1099: for (int i = 0; i < s.length; i++) {
1100: if (s[i].startsWith("/")) {
1101: foldersString += s[i] + ",";
1102: }
1103: }
1104: return foldersString;
1105: }
1106:
1107: public String getDescription() {
1108: if (entry.containsKey(BOOKMARK_DESCRIPTION)) {
1109: return entry.get(BOOKMARK_DESCRIPTION);
1110: }
1111: return "";
1112: }
1113:
1114: public String getTitle() {
1115: if (entry.containsKey(BOOKMARK_TITLE)) {
1116: return entry.get(BOOKMARK_TITLE);
1117: }
1118: return entry.get(BOOKMARK_URL);
1119: }
1120:
1121: public String getOwner() {
1122: if (entry.containsKey(BOOKMARK_OWNER)) {
1123: return entry.get(BOOKMARK_OWNER);
1124: }
1125: return null; //null means admin
1126: }
1127:
1128: public void setOwner(String owner) {
1129: entry.put(BOOKMARK_OWNER, owner);
1130: }
1131:
1132: public boolean getPublic() {
1133: if (entry.containsKey(BOOKMARK_PUBLIC)) {
1134: return entry.get(BOOKMARK_PUBLIC).equals("public");
1135: }
1136: return false;
1137: }
1138:
1139: public boolean getFeed() {
1140: if (entry.containsKey(BOOKMARK_IS_FEED)) {
1141: return entry.get(BOOKMARK_IS_FEED).equals("true");
1142: }
1143: return false;
1144: }
1145:
1146: public void setPublic(boolean isPublic) {
1147: if (isPublic) {
1148: entry.put(BOOKMARK_PUBLIC, "public");
1149: } else {
1150: entry.put(BOOKMARK_PUBLIC, "private");
1151: }
1152: }
1153:
1154: public void setFeed(boolean isFeed) {
1155: if (isFeed) {
1156: entry.put(BOOKMARK_IS_FEED, "true");
1157: } else {
1158: entry.put(BOOKMARK_IS_FEED, "false");
1159: }
1160: }
1161:
1162: public void setProperty(String name, String value) {
1163: entry.put(name, value);
1164: //setBookmarksTable();
1165: }
1166:
1167: public void addTag(String tag) {
1168: tags.add(tag);
1169: }
1170:
1171: /**
1172: * set the Tags of the bookmark, and write them into the tags table.
1173: * @param tags2 a ArrayList with the tags
1174: */
1175: public void setTags(Set<String> tags2) {
1176: setTags(tags2, true);
1177: }
1178:
1179: /**
1180: * set the Tags of the bookmark
1181: * @param tags ArrayList with the tagnames
1182: * @param local sets, whether the updated tags should be stored to tagsDB
1183: */
1184: public void setTags(Set<String> tags2, boolean local) {
1185: tags = tags2; // TODO: check if this is safe
1186: // tags.addAll(tags2); // in order for renameTag() to work I had to change this form 'add' to 'set'
1187: Iterator<String> it = tags.iterator();
1188: while (it.hasNext()) {
1189: String tagName = it.next();
1190: Tag tag = getTag(tagHash(tagName));
1191: if (tag == null) {
1192: tag = new Tag(tagName);
1193: }
1194: tag.addUrl(getUrlHash());
1195: if (local) {
1196: saveTag(tag);
1197: }
1198: }
1199: toMap();
1200: }
1201:
1202: public long getTimeStamp() {
1203: return timestamp;
1204: }
1205:
1206: public void setTimeStamp(long ts) {
1207: this .timestamp = ts;
1208: }
1209: }
1210:
1211: /**
1212: * Subclass of bookmarksDB, which provides the tagIterator object-type
1213: */
1214: public class tagIterator implements Iterator<Tag> {
1215: kelondroCloneableIterator<String> tagIter;
1216: bookmarksDB.Tag nextEntry;
1217:
1218: public tagIterator(boolean up) throws IOException {
1219: flushTagCache(); //XXX: This costs performace :-((
1220: this .tagIter = bookmarksDB.this .tagsTable.keys(up, false);
1221: this .nextEntry = null;
1222: }
1223:
1224: public boolean hasNext() {
1225: try {
1226: return this .tagIter.hasNext();
1227: } catch (kelondroException e) {
1228: //resetDatabase();
1229: return false;
1230: }
1231: }
1232:
1233: public Tag next() {
1234: try {
1235: return getTag(this .tagIter.next());
1236: } catch (kelondroException e) {
1237: //resetDatabase();
1238: return null;
1239: }
1240: }
1241:
1242: public void remove() {
1243: if (this .nextEntry != null) {
1244: try {
1245: String tagHash = this .nextEntry.getTagHash();
1246: if (tagHash != null)
1247: removeTag(tagHash);
1248: } catch (kelondroException e) {
1249: //resetDatabase();
1250: }
1251: }
1252: }
1253: }
1254:
1255: /**
1256: * Subclass of bookmarksDB, which provides the bookmarkIterator object-type
1257: */
1258: public class bookmarkIterator implements Iterator<Bookmark> {
1259: Iterator<String> bookmarkIter;
1260: bookmarksDB.Bookmark nextEntry;
1261:
1262: public bookmarkIterator(boolean up) throws IOException {
1263: //flushBookmarkCache(); //XXX: this will cost performance
1264: this .bookmarkIter = bookmarksDB.this .bookmarksTable.keys(
1265: up, false);
1266: this .nextEntry = null;
1267: }
1268:
1269: public boolean hasNext() {
1270: try {
1271: return this .bookmarkIter.hasNext();
1272: } catch (kelondroException e) {
1273: //resetDatabase();
1274: return false;
1275: }
1276: }
1277:
1278: public Bookmark next() {
1279: try {
1280: return getBookmark((String) this .bookmarkIter.next());
1281: } catch (kelondroException e) {
1282: //resetDatabase();
1283: return null;
1284: }
1285: }
1286:
1287: public void remove() {
1288: if (this .nextEntry != null) {
1289: try {
1290: Object bookmarkName = this .nextEntry.getUrlHash();
1291: if (bookmarkName != null)
1292: removeBookmark((String) bookmarkName);
1293: } catch (kelondroException e) {
1294: //resetDatabase();
1295: }
1296: }
1297: }
1298: }
1299:
1300: /**
1301: * Comparator to sort objects of type Bookmark according to their timestamps
1302: */
1303: public class bookmarkComparator implements Comparator<String> {
1304:
1305: private boolean newestFirst;
1306:
1307: /**
1308: * @param newestFirst newest first, or oldest first?
1309: */
1310: public bookmarkComparator(boolean newestFirst) {
1311: this .newestFirst = newestFirst;
1312: }
1313:
1314: public int compare(String obj1, String obj2) {
1315: Bookmark bm1 = getBookmark(obj1);
1316: Bookmark bm2 = getBookmark(obj2);
1317: if (bm1 == null || bm2 == null)
1318: return 0; //XXX: i think this should not happen? maybe this needs further tracing of the bug
1319: if (this .newestFirst) {
1320: if (bm2.getTimeStamp() - bm1.getTimeStamp() > 0)
1321: return 1;
1322: return -1;
1323: }
1324: if (bm1.getTimeStamp() - bm2.getTimeStamp() > 0)
1325: return 1;
1326: return -1;
1327: }
1328: }
1329:
1330: /**
1331: * Comparator to sort objects of type Tag according to their names
1332: */
1333: public class tagComparator implements Comparator<Tag> {
1334:
1335: public int compare(Tag obj1, Tag obj2) {
1336: return obj1.getTagName().compareTo(obj2.getTagName());
1337: }
1338:
1339: }
1340:
1341: public class tagSizeComparator implements Comparator<Tag> {
1342:
1343: public int compare(Tag obj1, Tag obj2) {
1344: if (obj1.size() < obj2.size())
1345: return 1;
1346: else if (obj1.getTagName().equals(obj2.getTagName()))
1347: return 0;
1348: else
1349: return -1;
1350: }
1351:
1352: }
1353: }
|