Author: michiel
Date: 2010-06-15 12:00:31 +0200 (Tue, 15 Jun 2010)
New Revision: 42572
Added:
mmbase/trunk/bridge/src/main/java/org/mmbase/datatypes/processors/KeywordsProcessor.java
Log:
Added:
mmbase/trunk/bridge/src/main/java/org/mmbase/datatypes/processors/KeywordsProcessor.java
===================================================================
---
mmbase/trunk/bridge/src/main/java/org/mmbase/datatypes/processors/KeywordsProcessor.java
(rev 0)
+++
mmbase/trunk/bridge/src/main/java/org/mmbase/datatypes/processors/KeywordsProcessor.java
2010-06-15 10:00:31 UTC (rev 42572)
@@ -0,0 +1,215 @@
+/*
+
+This software is OSI Certified Open Source Software.
+OSI Certified is a certification mark of the Open Source Initiative.
+
+The license (Mozilla version 1.0) can be read at the MMBase site.
+See http://www.MMBase.org/license
+
+*/
+package org.mmbase.datatypes.processors;
+
+import org.mmbase.bridge.*;
+import org.mmbase.bridge.util.HugeNodeListIterator;
+import org.mmbase.core.event.*;
+import org.mmbase.util.*;
+import org.mmbase.storage.search.*;
+import java.util.concurrent.*;
+import java.util.*;
+import org.mmbase.util.logging.*;
+
+/**
+ * This processor can be used as a 'set' processor on keyword fields. It
maintains then a count on
+ * all keywords. These counts are aquirable via the static method {...@link
#getKeywords} (and via a
+ * function 'keywords' on the 'utils' set). Two properties can be set on this
+ * processor. The first one is the 'repository' which is a key with with to
store the Map with the
+ * counts. This ensures that you can use this processor for different 'clouds'
of keywords. The
+ * second one is the 'field', which is a String of the form <builder
name>.<field
+ * name>. This information cannot be aquired auomaticly because a field
knows about it's
+ * datatype, but not inversely. It is essential to use this last property.
+ *
+ * <pre><![CDATA<
+ <field name="keywords">
+ <gui>
+ <guiname xml:lang="nl">Sleutelwoorden</guiname>
+ <guiname xml:lang="en">Keywords</guiname>
+ </gui>
+ <datatype base="line" xmlns="http://www.mmbase.org/xmlns/datatypes">
+ <maxLength value="255" />
+ <setprocessor>
+ <class name="org.mmbase.datatypes.processors.KeywordsProcessor">
+ <param name="repository" value="episodes" />
+ <param name="field" value="episodes.keywords" />
+ </class>
+ </setprocessor>
+ </datatype>
+ </field>
+ >]></pre>
+ *
+ * @author Michiel Meeuwissen
+ * @version $Id: KeywordsProcessor.java 40288 2009-12-21 10:56:52Z michiel $
+ * @since MMBase-1.9
+ */
+
+public class KeywordsProcessor implements Processor, NodeEventListener {
+
+ private static final Logger log =
Logging.getLoggerInstance(KeywordsProcessor.class);
+
+ private static final long serialVersionUID = 1L;
+
+ private static Map<String, Map<String, Integer>> repositories = new
ConcurrentHashMap<String, Map<String, Integer>>();
+
+
+ /**
+ * Returns all keywords in a certain repository, as an unmodifiable
SortedSet of Map.Entry's. The most occuring on
+ * on top.
+ */
+ public static SortedSet<Map.Entry<String, Integer>> getKeywords(String
repository) {
+ Set<Map.Entry<String, Integer>> set =
repositories.get(repository).entrySet();
+ SortedSet<Map.Entry<String, Integer>> sorted = new
TreeSet<Map.Entry<String, Integer>>(new EntryComparator());
+ sorted.addAll(set);
+ return Collections.unmodifiableSortedSet(sorted);
+ }
+
+
+
+ private String repository = "keywords";
+ private String field = null;
+ private String builder = null;
+ /**
+ * Sets the 'repository' in which this processor will store its keyword
counts. This defaults to 'keywords'.
+ */
+ public void setRepository(String r) {
+ repository = r;
+ }
+
+ protected static Map<String, Integer> getRepository(String rep) {
+ if (rep == null || "".equals(rep)) rep = "keywords";
+ Map<String, Integer> keywords = repositories.get(rep);
+ if (keywords == null) {
+ keywords = new ConcurrentHashMap<String, Integer>();
+ repositories.put(rep, keywords);
+ }
+ return keywords;
+ }
+ /**
+ * Sets the 'field' on which this keywords processor is working.
+ */
+ public void setField(final String f) {
+ if (field == null) {
+ ThreadPools.jobsExecutor.execute(new Runnable() {
+ public void run() {
+ getRepository(repository);
+ log.service("Filling keyword repository for " + f);
+ String[] array = f.split("\\.");
+ builder = array[0];
+ field = array[1];
+
EventManager.getInstance().addEventListener(KeywordsProcessor.this);
+ ContextProvider.getDefaultCloudContext().assertUp();
+ Cloud cloud =
ContextProvider.getDefaultCloudContext().getCloud("mmbase", "class", null);
+ NodeManager nm = cloud.getNodeManager(builder);
+ NodeQuery q = nm.createQuery();
+ Constraint c =
q.createConstraint(q.createStepField(field));
+ q.setInverse(c, true);
+ q.setConstraint(c);
+ NodeIterator ni = new HugeNodeListIterator(q);
+ long i = 0;
+ while(ni.hasNext()) {
+ Node n = ni.nextNode();
+ String keywords = n.getStringValue(field);
+ i++;
+ if (i % 1000 == 0 && log.isDebugEnabled()) {
+ log.debug("Found keywords " +
getKeywords(repository));
+ }
+ if (! "".equals(keywords)) {
+ addKeywords(repository,
keywords.toLowerCase());
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Ready " + getKeywords(repository));
+ }
+ }
+ }
+ );
+ }
+ }
+
+ protected static void addKeywords(String repository, String k) {
+ Map<String, Integer> keywords = getRepository(repository);
+ for (String keyword : k.split(",")) {
+ keyword = keyword.trim().toLowerCase();
+ if (! "".equals(keyword)) {
+ Integer i = keywords.get(keyword);
+ if (i == null) {
+ i = 1;
+ } else {
+ i++;
+ }
+ keywords.put(keyword, i);
+ }
+ }
+ }
+ protected static void removeKeywords(String repository, String k) {
+ Map<String, Integer> keywords = getRepository(repository);
+ for (String keyword : k.split(",")) {
+ keyword = keyword.trim().toLowerCase();
+ if (! "".equals(keyword)) {
+ Integer i = keywords.get(keyword);
+ if (i == null) {
+ i = 0;
+ } else {
+ i--;
+ }
+ keywords.put(keyword, i);
+ }
+ }
+ }
+
+
+ protected void change(String oldValues, String newValues) {
+ log.debug("Changing keywords from " + oldValues + " to " + newValues);
+ if (oldValues != null) removeKeywords(repository, oldValues);
+ addKeywords(repository, newValues);
+ if (log.isDebugEnabled()) {
+ log.debug("Keywords now: " + getKeywords(repository));
+ }
+ }
+
+ public Object process(Node node, Field field, Object value) {
+ if (field == null) throw new IllegalStateException("Field property
should have been set");
+ String oldValues = node.getStringValue(field.getName());
+ String newValues = Casting.toString(value);
+ change(node.isNew() ? null : oldValues, newValues);
+ return value;
+ }
+
+ public void notify(NodeEvent event) {
+ if (! event.isLocal()) {
+ if (event.getBuilderName().equals(builder)) {
+ change(Casting.toString(event.getOldValue(field)),
+ Casting.toString(event.getNewValue(field)));
+ } else {
+ log.debug("ignoring event while of wrong type" + event);
+ }
+ } else {
+ log.debug("ignoring local event " + event);
+ }
+ }
+
+
+ static class EntryComparator implements Comparator<Map.Entry<String,
Integer>> {
+ public int compare(Map.Entry<String, Integer> o1, Map.Entry<String,
Integer> o2) {
+ int res = o2.getValue() - o1.getValue();
+ if (res != 0) return res;
+ return o2.getKey().compareTo(o1.getKey());
+ }
+ @Override
+ public boolean equals(Object o) {
+ return o != null && o instanceof EntryComparator;
+ }
+
+ }
+
+}
+
+
_______________________________________________
Cvs mailing list
[email protected]
http://lists.mmbase.org/mailman/listinfo/cvs