/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* !#
*/
package net.ontopia.topicmaps.utils.ctm;
import java.util.List;
import java.util.Stack;
import java.util.ArrayList;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.topicmaps.core.AssociationIF;
import net.ontopia.topicmaps.core.OccurrenceIF;
import net.ontopia.topicmaps.core.ReifiableIF;
import net.ontopia.topicmaps.core.ScopedIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapBuilderIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.TopicNameIF;
import net.ontopia.topicmaps.core.VariantNameIF;
import net.ontopia.topicmaps.utils.PSI;
import net.ontopia.topicmaps.utils.MergeUtils;
import net.ontopia.topicmaps.xml.InvalidTopicMapException;
/**
* INTERNAL: The event handler which actually builds the topic map.
* This handler assumes that all generators actually resolve to the
* correct values.
*/
public class BuilderEventHandler implements ParseEventHandlerIF {
private TopicMapBuilderIF builder;
private ParseContextIF context;
private Stack framestack; // for embedded topics
private ValueGeneratorIF generator;
private TopicIF previous_embedded;
// <framed>
// the topic of the current topic block, *or* the current embedded topic
private TopicIF topic;
// the topic name currently being parsed (needed for variant names)
private TopicNameIF name;
// the statement currently being parsed (used by the scope parsing code)
private ScopedIF scoped;
// the statement currently being parsed (used by the reifier parsing code)
private ReifiableIF reifiable;
// the association currently being parsed (needed for roles)
private AssociationIF association;
// </framed>
private TopicIF assoctype; // supertype-subtype
private TopicIF subtype; // subtype
private TopicIF supertype; // supertype
public BuilderEventHandler(TopicMapBuilderIF builder, ParseContextIF context) {
this.builder = builder;
this.context = context;
this.framestack = new Stack();
this.reifiable = builder.getTopicMap();
this.generator = new PreviousEmbeddedTopicGenerator();
}
public void startTopicItemIdentifier(ValueGeneratorIF locator) {
topic = context.makeTopicByItemIdentifier(locator.getLocator());
}
public void startTopicSubjectIdentifier(ValueGeneratorIF locator) {
topic = context.makeTopicBySubjectIdentifier(locator.getLocator());
}
public void startTopicSubjectLocator(ValueGeneratorIF locator) {
topic = context.makeTopicBySubjectLocator(locator.getLocator());
}
public void startTopic(ValueGeneratorIF topicgen) {
// this is a special situation, because while we might be passed a
// topic, we might also be passed just an IRI to be interpreted as
// a subject identifier
if (topicgen.isTopic())
topic = topicgen.getTopic();
else if (topicgen.getLocator() != null)
startTopicSubjectIdentifier(topicgen);
else
throw new InvalidTopicMapException("Wrong type passed as topic identifier: " + topicgen.getLiteral());
}
public void addItemIdentifier(ValueGeneratorIF locator) {
topic.addItemIdentifier(locator.getLocator());
}
public void addSubjectIdentifier(ValueGeneratorIF locator) {
TopicMapIF tm = builder.getTopicMap();
TopicIF other = tm.getTopicBySubjectIdentifier(locator.getLocator());
if (other != null)
merge(topic, other);
else
topic.addSubjectIdentifier(locator.getLocator());
}
public void addSubjectLocator(ValueGeneratorIF locator) {
topic.addSubjectLocator(locator.getLocator());
}
public void addTopicType(ValueGeneratorIF type) {
topic.addType(type.getTopic());
}
public void addSubtype(ValueGeneratorIF thesubtype) {
// get typing topics
if (assoctype == null)
assoctype = getTopicByPSI(PSI.getSAMSupertypeSubtype());
if (subtype == null)
subtype = getTopicByPSI(PSI.getSAMSubtype());
if (supertype == null)
supertype = getTopicByPSI(PSI.getSAMSupertype());
// make the assertion
AssociationIF assoc = builder.makeAssociation(assoctype);
builder.makeAssociationRole(assoc, subtype, topic);
builder.makeAssociationRole(assoc, supertype, thesubtype.getTopic());
}
public void startName(ValueGeneratorIF type, ValueGeneratorIF value) {
name = builder.makeTopicName(topic, type.getTopic(), value.getLiteral());
scoped = name;
reifiable = name;
}
public void addScopingTopic(ValueGeneratorIF topic) {
scoped.addTheme(topic.getTopic());
}
public void addReifier(ValueGeneratorIF topic) {
TopicIF reifier = topic.getTopic();
if (reifier.getReified() != null)
throw new InvalidTopicMapException("Cannot reify " + reifiable + " because "+
reifier + " already reifies " +
reifier.getReified());
reifiable.setReifier(reifier);
}
public void startVariant(ValueGeneratorIF value) {
// FIXME: no support for datatypes here yet...
VariantNameIF variant = builder.makeVariantName(name, value.getLiteral());
scoped = variant;
reifiable = variant;
}
public void endName() {
}
public void startOccurrence(ValueGeneratorIF type, ValueGeneratorIF value) {
OccurrenceIF occurrence =
builder.makeOccurrence(topic,
type.getTopic(),
value.getLiteral(),
value.getDatatype());
scoped = occurrence;
reifiable = occurrence;
}
public void endOccurrence() {
}
public void endTopic() {
topic = null; // so we can tell if we are in a block or not
}
public void startAssociation(ValueGeneratorIF type) {
association = builder.makeAssociation(type.getTopic());
scoped = association;
}
public void addRole(ValueGeneratorIF type, ValueGeneratorIF player) {
reifiable = builder.makeAssociationRole(association,
type.getTopic(),
player.getTopic());
}
public void endRoles() {
reifiable = association;
}
public void endAssociation() {
}
public void startEmbeddedTopic() {
framestack.push(new ParseFrame(topic, name, scoped, reifiable,
association));
topic = context.makeAnonymousTopic();
}
public ValueGeneratorIF endEmbeddedTopic() {
previous_embedded = topic;
ParseFrame frame = (ParseFrame) framestack.pop();
topic = frame.topic;
name = frame.name;
scoped = frame.scoped;
reifiable = frame.reifiable;
association = frame.association;
return generator;
}
public void templateInvocation(String name, List arguments) {
if (topic != null) {
// invocations inside topic blocks need to have the current topic prepended
// to the list of parameters. note that this needs to be as a generator.
// however, argument lists may be stored in GenericParseEvent objects, so
// to modify the argument list we must first copy it.
arguments = new ArrayList(arguments);
arguments.add(0, new ValueGenerator(topic, null, null, null));
}
Template template = context.getTemplate(name, arguments.size());
if (template == null)
throw new InvalidTopicMapException("Template '" + name + "' not declared"+
" with " + arguments.size() +
" parameters");
TopicIF tmp = topic; // template may end current topic block
template.invoke(arguments, this);
topic = tmp; // preserves current topic, if any
}
// --- Internal methods
private TopicIF getTopicByPSI(LocatorIF psi) {
return context.makeTopicBySubjectIdentifier(psi);
}
private void merge(TopicIF topic, TopicIF other) {
if (topic == other)
return;
// make sure hard-wired references to "ako" topics are not lost
if (assoctype == other)
assoctype = topic;
else if (subtype == other)
subtype = topic;
else if (supertype == other)
supertype = topic;
MergeUtils.mergeInto(topic, other);
}
// --- PreviousEmbeddedTopicGenerator
class PreviousEmbeddedTopicGenerator extends AbstractTopicGenerator {
public TopicIF getTopic() {
return previous_embedded;
}
public ValueGeneratorIF copy() {
return new ValueGenerator(previous_embedded, null, null, null);
}
}
}