cleanup / abuse system fix / prepping for a release
[mir.git] / source / mircoders / abuse / FilterEngine.java
index 7a61c85..1981946 100755 (executable)
-/*\r
- * Copyright (C) 2001, 2002 The Mir-coders group\r
- *\r
- * This file is part of Mir.\r
- *\r
- * Mir is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * Mir is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with Mir; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- *\r
- * In addition, as a special exception, The Mir-coders gives permission to link\r
- * the code of this program with  any library licensed under the Apache Software License,\r
- * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
- * (or with modified versions of the above that use the same license as the above),\r
- * and distribute linked combinations including the two.  You must obey the\r
- * GNU General Public License in all respects for all of the code used other than\r
- * the above mentioned libraries.  If you modify this file, you may extend this\r
- * exception to your version of the file, but you are not obligated to do so.\r
- * If you do not wish to do so, delete this exception statement from your version.\r
- */\r
-\r
-package mircoders.abuse;\r
-\r
-import mir.entity.Entity;\r
-import mir.entity.adapter.EntityAdapter;\r
-import mir.entity.adapter.EntityAdapterModel;\r
-import mir.entity.adapter.EntityIteratorAdapter;\r
-import mir.log.LoggerWrapper;\r
-import mir.storage.StorageObjectExc;\r
-import mir.session.Request;\r
-import mir.config.MirPropertiesConfiguration;\r
-import mircoders.storage.DatabaseFilter;\r
-import mircoders.storage.DatabaseFilterGroup;\r
-import mircoders.global.MirGlobal;\r
-\r
-import java.util.*;\r
-import java.text.SimpleDateFormat;\r
-\r
-public class FilterEngine {\r
-  private Map filterTypes;\r
-  private List filterTypeIds;\r
-\r
-  private List filterGroups;\r
-  private Map idToFilterGroup;\r
-  private LoggerWrapper logger;\r
-  private EntityAdapterModel model;\r
-  private SimpleDateFormat dateFormat;\r
-  private MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();\r
-\r
-  public FilterEngine(EntityAdapterModel aModel) {\r
-    logger = new LoggerWrapper("Global.Abuse.FilterEngine");\r
-    filterGroups = new ArrayList();\r
-    idToFilterGroup = new HashMap();\r
-\r
-    filterTypes = new HashMap();\r
-    filterTypeIds = new ArrayList();\r
-    try {\r
-      Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();\r
-      while (i.hasNext()) {\r
-        FilterType filterType = (FilterType) i.next();\r
-\r
-        filterTypes.put(filterType.getName(), filterType);\r
-        filterTypeIds.add(filterType.getName());\r
-      }\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException(t.getMessage());\r
-    }\r
-\r
-    model = aModel;\r
-\r
-    dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");\r
-    dateFormat.setTimeZone(TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone")));\r
-    reload();\r
-  }\r
-\r
-  public Filter testPosting(Entity anEntity, Request aRequest) {\r
-    Iterator i = filterGroups.iterator();\r
-    while (i.hasNext()) {\r
-      FilterGroup group = (FilterGroup) i.next();\r
-      Iterator j = group.getFilters().iterator();\r
-      while (j.hasNext()) {\r
-        Filter filter = (Filter) j.next();\r
-        try {\r
-          if (filter.test(anEntity, aRequest)) {\r
-            return filter;\r
-          }\r
-        }\r
-        catch (Throwable t) {\r
-          logger.warn("Exception thrown while testing filter " + filter.getType() + " ( " + filter.getExpression() + ") " + t.toString());\r
-        }\r
-      }\r
-    }\r
-\r
-    return null;\r
-  }\r
-\r
-  public List getFilterTypes() {\r
-    try {\r
-      List result = new ArrayList();\r
-\r
-      Iterator i = filterTypeIds.iterator();\r
-      while (i.hasNext()) {\r
-        String id = (String) i.next();\r
-\r
-        Map action = new HashMap();\r
-        action.put("resource", id);\r
-        action.put("identifier", id);\r
-\r
-        result.add(action);\r
-      }\r
-\r
-      return result;\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException("can't get article actions");\r
-    }\r
-  }\r
-\r
-  public class FilterGroup {\r
-    private List filters;\r
-    private Entity entity;\r
-    private Map idToFilter;\r
-\r
-    public FilterGroup(Entity anEntity) {\r
-      this (anEntity, Collections.EMPTY_LIST);\r
-    }\r
-\r
-    public FilterGroup(Entity anEntity, List aFilters) {\r
-      entity = anEntity;\r
-      filters = new ArrayList();\r
-      idToFilter = new HashMap();\r
-      Iterator i = aFilters.iterator();\r
-\r
-      while (i.hasNext()) {\r
-        Entity entity = (Entity) i.next();\r
-        try {\r
-          Filter filter = new Filter(entity);\r
-          introduceFilter(filter);\r
-        }\r
-        catch (AbuseExc e) {\r
-        }\r
-      }\r
-    }\r
-\r
-    public Entity getEntity() {\r
-      return entity;\r
-    }\r
-\r
-    public EntityAdapter getEntityAdapter() {\r
-      return model.makeEntityAdapter("filterGroup",  entity);\r
-    }\r
-\r
-    public List getFilterEntityAdapterList() {\r
-      List result = new ArrayList();\r
-\r
-      Iterator i = filters.iterator();\r
-      while (i.hasNext()) {\r
-        Filter filter = (Filter) i.next();\r
-        result.add(filter.getEntityAdapter());\r
-      }\r
-\r
-      return result;\r
-    }\r
-\r
-    public List getFilters() {\r
-      return filters;\r
-    }\r
-\r
-    public Filter getFilterForId(String anId) {\r
-      Filter result = (Filter) idToFilter.get(anId);\r
-      if (result==null) {\r
-        throw new NullPointerException("No such filter");\r
-      }\r
-\r
-      return result;\r
-    }\r
-\r
-    private void introduceFilter(Filter aFilter) {\r
-      filters.add(aFilter);\r
-      idToFilter.put(aFilter.getEntity().getId(), aFilter);\r
-    }\r
-\r
-    private void removeFilter(Filter aFilter) {\r
-      filters.remove(aFilter);\r
-      idToFilter.remove(aFilter.getEntity().getId());\r
-    }\r
-\r
-    private void deleteFilter(String anId) {\r
-      Filter filter = getFilterForId(anId);\r
-      removeFilter(filter);\r
-      DatabaseFilter.getInstance().delete(anId);\r
-    }\r
-\r
-    public void populateFilterEntity(Entity anEntity, String aType, String anExpression,\r
-                             String aComments, String aTag, String anArticleAction,\r
-                             String aCommentAction) {\r
-\r
-      anEntity.setFieldValue("type", aType);\r
-      anEntity.setFieldValue("expression", anExpression);\r
-      anEntity.setFieldValue("comment", aComments);\r
-      anEntity.setFieldValue("tag", aTag);\r
-      anEntity.setFieldValue("articleaction", anArticleAction);\r
-      anEntity.setFieldValue("commentaction", aCommentAction);\r
-    }\r
-\r
-    public String updateFilter(String anId, String aType, String anExpression,\r
-                             String aComments, String aTag, String anArticleAction,\r
-                             String aCommentAction) {\r
-\r
-      try {\r
-        getFilterTypeForId(aType).constructFilterInstance(anExpression);\r
-      }\r
-      catch (AbuseExc e) {\r
-        return e.getMessage();\r
-      }\r
-\r
-      Entity entity = getFilterForId(anId).getEntity();\r
-      populateFilterEntity(entity, aType, anExpression, aComments, aTag,\r
-          anArticleAction, aCommentAction);\r
-      entity.update();\r
-\r
-      return "";\r
-    }\r
-\r
-    public String createFilter(String aType, String anExpression,\r
-                             String aComments, String aTag, String anArticleAction,\r
-                             String aCommentAction) throws StorageObjectExc {\r
-      FilterInstance instance;\r
-\r
-      try {\r
-        instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);\r
-      }\r
-      catch (AbuseExc e) {\r
-        return e.getMessage();\r
-      }\r
-\r
-      Entity entity = DatabaseFilter.getInstance().createNewEntity();\r
-      populateFilterEntity(entity, aType, anExpression, aComments, aTag,\r
-          anArticleAction, aCommentAction);\r
-      entity.setFieldValue("priority", "1");\r
-      entity.setFieldValue("filter_group_id", getEntity().getId());\r
-      entity.insert();\r
-\r
-      Filter filter = new Filter(entity, instance);\r
-      introduceFilter(filter);\r
-\r
-      return "";\r
-    }\r
-\r
-    public String getName() {\r
-      return entity.getFieldValue("name");\r
-    }\r
-  }\r
-\r
-  public class Filter {\r
-    private Entity entity;\r
-    private FilterInstance instance;\r
-\r
-    public Filter(Entity anEntity) throws AbuseExc {\r
-      this(anEntity, getFilterTypeForId(anEntity.getFieldValue("type")).constructFilterInstance(anEntity.getFieldValue("expression")));\r
-    }\r
-\r
-    public Filter(Entity anEntity, FilterInstance anInstance) {\r
-      entity = anEntity;\r
-      instance = anInstance;\r
-    }\r
-\r
-    public Entity getEntity() {\r
-      return entity;\r
-    }\r
-\r
-    public EntityAdapter getEntityAdapter() {\r
-      return model.makeEntityAdapter("filter", entity);\r
-    }\r
-\r
-    public void update(String aType, String anExpression, String aComments, String aTag,\r
-                       String anArticleAction, String aCommentAction) throws AbuseExc {\r
-\r
-      instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);\r
-\r
-      entity.setFieldValue("type", aType);\r
-      entity.setFieldValue("expression", anExpression);\r
-      entity.setFieldValue("tag", aType);\r
-      entity.setFieldValue("comment", aComments);\r
-      entity.setFieldValue("articleaction", anArticleAction);\r
-      entity.setFieldValue("commentaction", aCommentAction);\r
-      entity.setFieldValue("last_hit", null);\r
-      entity.update();\r
-   }\r
-\r
-    public void updateLastHit(Date aDate) {\r
-      entity.setFieldValue("last_hit", dateFormat.format(aDate));\r
-      entity.update();\r
-    }\r
-\r
-    public String getType() {\r
-      return entity.getFieldValue("type");\r
-    }\r
-\r
-    public String getExpression() {\r
-      return entity.getFieldValue("expression");\r
-    }\r
-\r
-    public String getTag() {\r
-      return entity.getFieldValue("tag");\r
-    }\r
-\r
-    public String getComment() {\r
-      return entity.getFieldValue("comment");\r
-    }\r
-\r
-    public String getArticleAction() {\r
-      return entity.getFieldValue("articleaction");\r
-    }\r
-\r
-    public String getCommentAction() {\r
-      return entity.getFieldValue("commentaction");\r
-    }\r
-\r
-    public FilterInstance getInstance() {\r
-      return instance;\r
-    }\r
-\r
-    public boolean test(Entity anEntity, Request aRequest) {\r
-      return instance.test(anEntity, aRequest);\r
-    }\r
-  }\r
-\r
-  public synchronized void reload() {\r
-    filterGroups.clear();\r
-    idToFilterGroup.clear();\r
-\r
-    try {\r
-      Iterator i = new EntityIteratorAdapter("", "priority asc", 100, model, "filterGroup");\r
-      while (i.hasNext()) {\r
-        EntityAdapter entityAdapter = (EntityAdapter) i.next();\r
-        List filters = new ArrayList();\r
-        Iterator j = (Iterator) entityAdapter.get("to_filters");\r
-        while (j.hasNext()) {\r
-          filters.add(((EntityAdapter) j.next()).getEntity());\r
-        }\r
-\r
-        FilterGroup filterGroup = new FilterGroup(entityAdapter.getEntity(), filters);\r
-        introduceFilterGroup(filterGroup);\r
-      }\r
-    }\r
-    catch (Throwable e) {\r
-      logger.error("Can't load filters: " + e.getMessage());\r
-    }\r
-  }\r
-\r
-  public synchronized List getFilterGroups() {\r
-    List result = new ArrayList();\r
-    Iterator i = filterGroups.iterator();\r
-    while (i.hasNext()) {\r
-      result.add(((FilterGroup) i.next()).getEntityAdapter());\r
-    }\r
-\r
-    return result;\r
-  }\r
-\r
-  public synchronized void updateFilterGroup(String anId, String aName) {\r
-    FilterGroup filterGroup = getFilterGroupForId(anId);\r
-    filterGroup.getEntity().setFieldValue("name", aName);\r
-    filterGroup.getEntity().update();\r
-  }\r
-\r
-  public synchronized void addFilterGroup(String aName) throws StorageObjectExc {\r
-    Entity entity = DatabaseFilterGroup.getInstance().createNewEntity();\r
-    entity.setFieldValue("name", aName);\r
-    entity.setFieldValue("priority", "1");\r
-    entity.insert();\r
-\r
-    FilterGroup filterGroup = new FilterGroup(entity);\r
-    introduceFilterGroup(filterGroup);\r
-  }\r
-\r
-  public synchronized void deleteFilterGroup(String anId) {\r
-\r
-    FilterGroup filterGroup = getFilterGroupForId(anId);\r
-    removeFilterGroup(filterGroup);\r
-    DatabaseFilter.getInstance().deleteByWhereClause("filter_group_id = " + anId);\r
-    DatabaseFilterGroup.getInstance().delete(anId);\r
-  }\r
-\r
-  public synchronized void deleteFilter(String aGroupId, String anId) {\r
-    getFilterGroupForId(aGroupId).deleteFilter(anId);\r
-  }\r
-\r
-\r
-  public synchronized String updateFilter(String aGroupId, String anId,\r
-                                        String aType, String anExpression,\r
-                                        String aComments,\r
-                                        String aTag,\r
-                                        String anArticleAction,\r
-                                        String aCommentAction) {\r
-    return getFilterGroupForId(aGroupId).updateFilter(anId, aType,\r
-        anExpression, aComments, aTag, anArticleAction, aCommentAction);\r
-  }\r
-\r
-  public synchronized String addFilter(String aGroupId,\r
-                                       String aType, String anExpression,\r
-                                       String aComments,\r
-                                       String aTag,\r
-                                       String anArticleAction,\r
-                                       String aCommentAction) throws StorageObjectExc {\r
-    return getFilterGroupForId(aGroupId).createFilter(aType, anExpression,\r
-        aComments, aTag, anArticleAction, aCommentAction);\r
-  }\r
-\r
-\r
-  public FilterGroup getFilterGroupForId(String anId) {\r
-    FilterGroup result = (FilterGroup) idToFilterGroup.get(anId);\r
-    if (result == null) {\r
-      throw new NullPointerException("No such filter group");\r
-    }\r
-\r
-    return result;\r
-  }\r
-\r
-  public Filter getFilterForId(String aGroupId, String anId) {\r
-    return getFilterGroupForId(aGroupId).getFilterForId(anId);\r
-  }\r
-\r
-\r
-  public List getFilters(String aFilterGroupId) {\r
-    return getFilterGroupForId(aFilterGroupId).getFilterEntityAdapterList();\r
-  }\r
-\r
-  private void introduceFilterGroup(FilterGroup aFilterGroup) {\r
-    filterGroups.add(aFilterGroup);\r
-    idToFilterGroup.put(aFilterGroup.getEntity().getId(), aFilterGroup);\r
-  }\r
-\r
-  private void removeFilterGroup(FilterGroup aFilterGroup) {\r
-    filterGroups.remove(aFilterGroup);\r
-    idToFilterGroup.remove(aFilterGroup.getEntity().getId());\r
-  }\r
-\r
-  private FilterType getFilterTypeForId(String anId) {\r
-    return (FilterType) filterTypes.get(anId);\r
-  }\r
-}\r
+/*
+ * Copyright (C) 2001, 2002 The Mir-coders group
+ *
+ * This file is part of Mir.
+ *
+ * Mir 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 of the License, or
+ * (at your option) any later version.
+ *
+ * Mir 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 Mir; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * In addition, as a special exception, The Mir-coders gives permission to link
+ * the code of this program with  any library licensed under the Apache Software License,
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
+ * (or with modified versions of the above that use the same license as the above),
+ * and distribute linked combinations including the two.  You must obey the
+ * GNU General Public License in all respects for all of the code used other than
+ * the above mentioned libraries.  If you modify this file, you may extend this
+ * exception to your version of the file, but you are not obligated to do so.
+ * If you do not wish to do so, delete this exception statement from your version.
+ */
+
+package mircoders.abuse;
+
+import mir.config.MirPropertiesConfiguration;
+import mir.entity.Entity;
+import mir.entity.adapter.EntityAdapter;
+import mir.entity.adapter.EntityAdapterModel;
+import mir.entity.adapter.EntityIteratorAdapter;
+import mir.log.LoggerWrapper;
+import mir.session.Request;
+import mir.storage.DatabaseExc;
+import mircoders.global.MirGlobal;
+import mircoders.storage.DatabaseFilter;
+import mircoders.storage.DatabaseFilterGroup;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+/** The FilterEngine manages a list of all filters and filter groups.
+ *  Use the testPosting() method to apply all filters on an 
+ *  Entity (for ex. an article or a comment)
+ */
+public class FilterEngine {
+  private Map filterTypes;
+  private List filterTypeIds;
+
+  private List filterGroups;
+  private Map idToFilterGroup;
+  private LoggerWrapper logger;
+  private EntityAdapterModel model;
+  private SimpleDateFormat dateFormat;
+  private MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
+
+  public FilterEngine(EntityAdapterModel aModel) {
+    logger = new LoggerWrapper("Global.Abuse.FilterEngine");
+    filterGroups = new ArrayList();
+    idToFilterGroup = new HashMap();
+
+    filterTypes = new HashMap();
+    filterTypeIds = new ArrayList();
+    try {
+      Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();
+      while (i.hasNext()) {
+        FilterType filterType = (FilterType) i.next();
+
+        filterTypes.put(filterType.getName(), filterType);
+        filterTypeIds.add(filterType.getName());
+      }
+    }
+    catch (Throwable t) {
+      throw new RuntimeException(t.getMessage());
+    }
+
+    model = aModel;
+
+    dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    dateFormat.setTimeZone(TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone")));
+    reload();
+  }
+  /** applies all filters from all filter groups to an Entity.
+   *  The entity may be, for example, an article, or a comment.
+   *  It returns a filter that matches if it finds one, null otherwise  
+   */
+  public Filter testPosting(Entity anEntity, Request aRequest) {
+    Iterator i = filterGroups.iterator();
+    while (i.hasNext()) {
+      FilterGroup group = (FilterGroup) i.next();
+      Iterator j = group.getFilters().iterator();
+      while (j.hasNext()) {
+        Filter filter = (Filter) j.next();
+        try {
+          if (filter.test(anEntity, aRequest)) {
+            return filter;
+          }
+        }
+        catch (Throwable t) {
+          logger.warn("Exception thrown while testing filter " + filter.getType() + " ( " + filter.getExpression() + ") " + t.toString());
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public List getFilterTypes() {
+    try {
+      List result = new ArrayList();
+
+      Iterator i = filterTypeIds.iterator();
+      while (i.hasNext()) {
+        String id = (String) i.next();
+
+        Map action = new HashMap();
+        action.put("resource", id);
+        action.put("identifier", id);
+
+        result.add(action);
+      }
+
+      return result;
+    }
+    catch (Throwable t) {
+      throw new RuntimeException("can't get article actions");
+    }
+  }
+  /** This class reflects a row of the filter_group 
+   * database table. Filters groups allow you to organize 
+   * your filters. For example: group1=spammer ,  
+   * group2=nazis etc.  
+   */
+  public class FilterGroup {
+    private List filters;
+    private Entity entity;
+    private Map idToFilter;
+
+    public FilterGroup(Entity anEntity) {
+      this (anEntity, Collections.EMPTY_LIST);
+    }
+
+    public FilterGroup(Entity anEntity, List aFilters) {
+      entity = anEntity;
+      filters = new ArrayList();
+      idToFilter = new HashMap();
+      Iterator i = aFilters.iterator();
+
+      while (i.hasNext()) {
+        Entity entity = (Entity) i.next();
+        try {
+          Filter filter = new Filter(entity);
+          introduceFilter(filter);
+        }
+        catch (AbuseExc e) {
+        }
+      }
+    }
+
+    public Entity getEntity() {
+      return entity;
+    }
+
+    public EntityAdapter getEntityAdapter() {
+      return model.makeEntityAdapter("filterGroup",  entity);
+    }
+
+    public List getFilterEntityAdapterList() {
+      List result = new ArrayList();
+
+      Iterator i = filters.iterator();
+      while (i.hasNext()) {
+        Filter filter = (Filter) i.next();
+        result.add(filter.getEntityAdapter());
+      }
+
+      return result;
+    }
+
+    public List getFilters() {
+      return filters;
+    }
+
+    public Filter getFilterForId(String anId) {
+      Filter result = (Filter) idToFilter.get(anId);
+      if (result==null) {
+        throw new NullPointerException("No such filter");
+      }
+
+      return result;
+    }
+
+    private void introduceFilter(Filter aFilter) {
+      filters.add(aFilter);
+      idToFilter.put(aFilter.getEntity().getId(), aFilter);
+    }
+
+    private void removeFilter(Filter aFilter) {
+      filters.remove(aFilter);
+      idToFilter.remove(aFilter.getEntity().getId());
+    }
+
+    private void deleteFilter(String anId) {
+      Filter filter = getFilterForId(anId);
+      removeFilter(filter);
+      DatabaseFilter.getInstance().delete(anId);
+    }
+
+    public void populateFilterEntity(Entity anEntity, String aType, String anExpression,
+                             String aComments, String aTag, String anArticleAction,
+                             String aCommentAction) {
+
+      anEntity.setFieldValue("type", aType);
+      anEntity.setFieldValue("expression", anExpression);
+      anEntity.setFieldValue("comment", aComments);
+      anEntity.setFieldValue("tag", aTag);
+      anEntity.setFieldValue("articleaction", anArticleAction);
+      anEntity.setFieldValue("commentaction", aCommentAction);
+    }
+
+    public String updateFilter(String anId, String aType, String anExpression,
+                             String aComments, String aTag, String anArticleAction,
+                             String aCommentAction) {
+
+      try {
+        getFilterTypeForId(aType).constructFilterInstance(anExpression);
+      }
+      catch (AbuseExc e) {
+        return e.getMessage();
+      }
+
+      Entity entity = getFilterForId(anId).getEntity();
+      populateFilterEntity(entity, aType, anExpression, aComments, aTag,
+          anArticleAction, aCommentAction);
+      entity.update();
+
+      return "";
+    }
+
+    public String createFilter(String aType, String anExpression,
+                             String aComments, String aTag, String anArticleAction,
+                             String aCommentAction) throws DatabaseExc {
+      FilterInstance instance;
+
+      try {
+        instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
+      }
+      catch (AbuseExc e) {
+        return e.getMessage();
+      }
+
+      Entity entity = DatabaseFilter.getInstance().createNewEntity();
+      populateFilterEntity(entity, aType, anExpression, aComments, aTag,
+          anArticleAction, aCommentAction);
+      entity.setFieldValue("priority", "1");
+      entity.setFieldValue("filter_group_id", getEntity().getId());
+      entity.insert();
+
+      Filter filter = new Filter(entity, instance);
+      introduceFilter(filter);
+
+      return "";
+    }
+
+    public String getName() {
+      return entity.getFieldValue("name");
+    }
+  }
+  
+  /** This class reflects a row of the filter database table. 
+   * To actually run a filter on data, use the test() method. 
+   * This class will automatically retreive and use the correct 
+   * filter type.
+   */
+  public class Filter {
+    private Entity entity;
+    private FilterInstance instance;
+
+    public Filter(Entity anEntity) throws AbuseExc {
+      this(anEntity, getFilterTypeForId(anEntity.getFieldValue("type")).constructFilterInstance(anEntity.getFieldValue("expression")));
+    }
+
+    public Filter(Entity anEntity, FilterInstance anInstance) {
+      entity = anEntity;
+      instance = anInstance;
+    }
+
+    public Entity getEntity() {
+      return entity;
+    }
+
+    public EntityAdapter getEntityAdapter() {
+      return model.makeEntityAdapter("filter", entity);
+    }
+
+    public void update(String aType, String anExpression, String aComments, String aTag,
+                       String anArticleAction, String aCommentAction) throws AbuseExc {
+
+      instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
+
+      entity.setFieldValue("type", aType);
+      entity.setFieldValue("expression", anExpression);
+      entity.setFieldValue("tag", aType);
+      entity.setFieldValue("comment", aComments);
+      entity.setFieldValue("articleaction", anArticleAction);
+      entity.setFieldValue("commentaction", aCommentAction);
+      entity.setFieldValue("last_hit", null);
+      entity.update();
+   }
+
+    public void updateLastHit(Date aDate) {
+      entity.setFieldValue("last_hit", dateFormat.format(aDate));
+      entity.update();
+    }
+
+    public String getType() {
+      return entity.getFieldValue("type");
+    }
+
+    public String getExpression() {
+      return entity.getFieldValue("expression");
+    }
+
+    public String getTag() {
+      return entity.getFieldValue("tag");
+    }
+
+    public String getComment() {
+      return entity.getFieldValue("comment");
+    }
+
+    public String getArticleAction() {
+      return entity.getFieldValue("articleaction");
+    }
+
+    public String getCommentAction() {
+      return entity.getFieldValue("commentaction");
+    }
+
+    public FilterInstance getInstance() {
+      return instance;
+    }
+
+    public boolean test(Entity anEntity, Request aRequest) {
+      return instance.test(anEntity, aRequest);
+    }
+  }
+
+  public synchronized void reload() {
+    filterGroups.clear();
+    idToFilterGroup.clear();
+
+    try {
+      Iterator i = new EntityIteratorAdapter("", "priority asc", 100, model, "filterGroup");
+      while (i.hasNext()) {
+        EntityAdapter entityAdapter = (EntityAdapter) i.next();
+        List filters = new ArrayList();
+        Iterator j = (Iterator) entityAdapter.getIterator("to_filters");
+        while (j.hasNext()) {
+          filters.add(((EntityAdapter) j.next()).getEntity());
+        }
+
+        FilterGroup filterGroup = new FilterGroup(entityAdapter.getEntity(), filters);
+        introduceFilterGroup(filterGroup);
+      }
+    }
+    catch (Throwable e) {
+      logger.error("Can't load filters: " + e.getMessage());
+    }
+  }
+
+  public synchronized List getFilterGroups() {
+    List result = new ArrayList();
+    Iterator i = filterGroups.iterator();
+    while (i.hasNext()) {
+      result.add(((FilterGroup) i.next()).getEntityAdapter());
+    }
+
+    return result;
+  }
+
+  public synchronized void updateFilterGroup(String anId, String aName) {
+    FilterGroup filterGroup = getFilterGroupForId(anId);
+    filterGroup.getEntity().setFieldValue("name", aName);
+    filterGroup.getEntity().update();
+  }
+
+  public synchronized void addFilterGroup(String aName) throws DatabaseExc {
+    Entity entity = DatabaseFilterGroup.getInstance().createNewEntity();
+    entity.setFieldValue("name", aName);
+    entity.setFieldValue("priority", "1");
+    entity.insert();
+
+    FilterGroup filterGroup = new FilterGroup(entity);
+    introduceFilterGroup(filterGroup);
+  }
+
+  public synchronized void deleteFilterGroup(String anId) {
+
+    FilterGroup filterGroup = getFilterGroupForId(anId);
+    removeFilterGroup(filterGroup);
+    DatabaseFilter.getInstance().deleteByWhereClause("filter_group_id = " + anId);
+    DatabaseFilterGroup.getInstance().delete(anId);
+  }
+
+  public synchronized void deleteFilter(String aGroupId, String anId) {
+    getFilterGroupForId(aGroupId).deleteFilter(anId);
+  }
+
+
+  public synchronized String updateFilter(String aGroupId, String anId,
+                                        String aType, String anExpression,
+                                        String aComments,
+                                        String aTag,
+                                        String anArticleAction,
+                                        String aCommentAction) {
+    return getFilterGroupForId(aGroupId).updateFilter(anId, aType,
+        anExpression, aComments, aTag, anArticleAction, aCommentAction);
+  }
+
+  public synchronized String addFilter(String aGroupId,
+                                       String aType, String anExpression,
+                                       String aComments,
+                                       String aTag,
+                                       String anArticleAction,
+                                       String aCommentAction) throws DatabaseExc {
+    return getFilterGroupForId(aGroupId).createFilter(aType, anExpression,
+        aComments, aTag, anArticleAction, aCommentAction);
+  }
+
+
+  public FilterGroup getFilterGroupForId(String anId) {
+    FilterGroup result = (FilterGroup) idToFilterGroup.get(anId);
+    if (result == null) {
+      throw new NullPointerException("No such filter group");
+    }
+
+    return result;
+  }
+
+  public Filter getFilterForId(String aGroupId, String anId) {
+    return getFilterGroupForId(aGroupId).getFilterForId(anId);
+  }
+
+
+  public List getFilters(String aFilterGroupId) {
+    return getFilterGroupForId(aFilterGroupId).getFilterEntityAdapterList();
+  }
+
+  private void introduceFilterGroup(FilterGroup aFilterGroup) {
+    filterGroups.add(aFilterGroup);
+    idToFilterGroup.put(aFilterGroup.getEntity().getId(), aFilterGroup);
+  }
+
+  private void removeFilterGroup(FilterGroup aFilterGroup) {
+    filterGroups.remove(aFilterGroup);
+    idToFilterGroup.remove(aFilterGroup.getEntity().getId());
+  }
+
+  private FilterType getFilterTypeForId(String anId) {
+    return (FilterType) filterTypes.get(anId);
+  }
+}