Create a class extending the abstract class SubtreeHandler.
SubtreeHandler has only one abstract method. The non-abstract methods are described further down.
public abstract Collection<Message> create(KnowWEArticle article, Section s);
The Section is a section of the type the handler is registered for, the KnowWEArticle is the article that called this method with this specific Section. The returned Collection of Messages needs to contain the Messages produced by the SubTreeHandler (read more about it in the chapter about Messages further below).
public void addSubtreeHandler([Priority p,] SubtreeHandler handler);
The attribute Priority in this method is optional. If you don't specify a priority, the handler will be added with default priority.
QuestionsSection, XCList, XCList, Include, RulesSection, SolutionsSection, ArticleWith this list, the map of lists sorted after priorites is created:
Priority.HIGHEST: SolutionsSection Priority.HIGHER: QuestionsSection Priority.DEFAULT: QuestionsSection, XCList, XCList, RulesSectionThis map now pretty much represents the order the SubtreeHandlers are executed in. KnowWE will simply iterate in descending priority-order over the lists of the map and executes all SubtreeHandlers of the priority of the list that is currently iterated. If the Type of the Section also contains handlers with a different priority, these handlers will not be executed until the list with the corresponding priority is iterated.
public Collection<Message> create(Article article, Section s) { doThis(); doThat(); if (a) { return Arrays.asList((Message) new KDOMNotice("Object created"); } else { return Arrays.asList((Message) new KDOMError("Object NOT created"); } }
public Collection<Message> create(Article article, Section s) { doThis(); List<Message> msgs = new ArrayList<Message>(); if (a && b) { msgs.add(new KDOMNotice("Object created"); msgs.add(new KDOMWarning("Minor syntax error"); } else { msgs.add(new KDOMError("Object NOT created"); } doThat(); return msgs; }
public Collection<Message> create(Article article, Section s) { doThis(); if (b) { return Arrays.asList((Message) new KDOMError("Object NOT created"); } doThat(); // everything is ok, so there is no need for a Message?! return null; }
public Collection<Message> create(Article article, Section s) { doThis(); if (b) { return Arrays.asList((Message) new KDOMError("Object NOT created"); } doThat(); // everything is ok, so there is no need for a Message?! return new ArrayList<Message>(0); }
public Collection<Message> create(Article article, Section s) { if (getKBM() == null) return null; doThis(); doThat(); if (a) { return Arrays.asList((Message) new KDOMNotice("Object created"); } else { return Arrays.asList((Message) new KDOMError("Object NOT created"); } }
If you want to use d3web, respectively a KnowledgeBase in a SubtreeHandler, you have to extend the abstract D3webSubtreeHandler in the KnowWE-Plugin-d3web. It provides the method D3webSubtreeHandler#getKBM(KnowWEArticle).
Example (implementing a D3webSubtreeHandler for the KnowWEObjectType QuestionDef):
public class CreateQuestionHandler extends QuestionTreeElementDefSubtreeHandler<QuestionDef> { @Override public Collection<Message> create(Article article, Section<QuestionDef> sec) { ... KnowledgeBaseManagement mgn = getKBM(article); mgn.createQuestionYN(sec.getOriginalText(), D3webUtils.findParent(sec, mgn)); ... } }
Attention: The use of
D3webModule.getKnowledgeRepresentationHandler(article.getWeb()).getKBM(article.getTitle());
Currently (summer 2010), incremental updating is under constant development, so things might change frequently. I will however try to point out some of the basics for implementing an incrementally working SubtreeHandler for KnowWE.
The abstract class SubtreeHandler provides two important methods:
If KnowWE builds an article for the first time or a full parse is performed, the method create(...) will create for every Section everything there is to create, for example a Question that is added to the KnowledgeBase.
If the article then is edited and the text of this Section, respectively this Question changes, the method destroy(...) is called for the last version of this Section or Question to destroy exactly this outdated version of the Question in the KnowledgeBase. The new, changed version of the Section or Question then will again call create(...) to replace the previousely removed Question with the new version.
Sections that haven't changed and are both in the last and the current version of the KDOM can now be skipped from creating and destroying, which saves a lot of time and is the reason for incremental updating in the first place.
To determine whether the currently revised Section needs to be created, respectively destroyed or if it just can be skipped, ConstraintModules are registered to the SubtreeHandler. They decide whether create(...), respectively destroy(...) needs to be called for the current Section.
The abstract class SubtreeHandler already has some ConstraintModules that provide the basic logic, so that create(...) and destroy(...) will be executed for all Sections that have changed themselves, changed their position in the KDOM or if currently simply an full parse is performed. For more advanced SubtreeHandlers it might however be necessary to register additional ConstraintModules to further specifiy, when to create and destroy and when not to. The ConstraintModules registered to the abstract SubtreeHandler are well commentated and might serve as an example, if you need to implement ConstraintModules yourself.
If you get the following message while running KnowWE and editing articles, it means, that the currently edited article uses a KnowWEObjectTypes that, on the one hand, doesn't implement the necessary destroy method to work incrementally, but on the other hand uses an component like d3web in the create method.
Example:
INFO: The following SubtreeHandlers prevent inrememental updating: [SolutionsSubTreeHandler, QuestionsSectionSubTreeHandler]
If there are handlers using certain components like d3web, but are not made for incremental updating, the incremental update needs to be aborted and replaced by a full parse of the article.
This full parse is achieved by calling setFullParse(true) on the article currently revising the Section in the destroy method of the SubtreeHandler. For d3web this is the default, as long as nobody overrides the destroy method of the D3webSubtreeHandler.
Don't hesitate to contact me directly if you have additional questions, problems or ideas regarding SubtreeHandlers: albrecht.striffler<at>gmx.de