All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.ontopia.topicmaps.utils.tmrap.TMRAPImplementation Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * #!
 * Ontopia TMRAP
 * #-
 * 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.tmrap;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.infoset.impl.basic.URILocator;
import net.ontopia.topicmaps.core.AssociationIF;
import net.ontopia.topicmaps.core.AssociationRoleIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapBuilderIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.TopicMapStoreIF;
import net.ontopia.topicmaps.impl.basic.InMemoryTopicMapStore;
import net.ontopia.topicmaps.nav2.core.NavigatorApplicationIF;
import net.ontopia.topicmaps.nav2.core.NavigatorRuntimeException;
import net.ontopia.topicmaps.query.core.QueryProcessorIF;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
import net.ontopia.topicmaps.query.utils.QueryUtils;
import net.ontopia.topicmaps.utils.TopicStringifiers;
import net.ontopia.topicmaps.utils.TopicMapSynchronizer;
import net.ontopia.topicmaps.utils.ltm.LTMTopicMapReader;
import net.ontopia.topicmaps.utils.ctm.CTMTopicMapReader;
import net.ontopia.topicmaps.xml.TMXMLWriter;
import net.ontopia.topicmaps.xml.XTMFragmentExporter;
import net.ontopia.topicmaps.xml.XTMTopicMapReader;
import net.ontopia.utils.OntopiaRuntimeException;

import org.xml.sax.DocumentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributeListImpl;

/**
 * PRIVATE: A generic implementation of the TMRAP protocol,
 * independent of any particular way of invoking the services. It is
 * used by both the plain HTTP and SOAP interfaces to TMRAP to provide
 * their TMRAP functionality.
 *
 * @since 3.1
 */
public class TMRAPImplementation {
  
  // Used to generate unique IDs
  private static long counter;

  // ===== GET-TOPIC =======================================================
  // -----------------------------------------------------------------------
  // | Parameter | Required? | Repeatable? | Type   | Value      | Default |
  // -----------------------------------------------------------------------
  // | item      |           | yes         | URI    | locator    |         |
  // | subject   |           | yes         | URI    | locator    |         |
  // | indicator |           | yes         | URI    | locator    |         |
  // | topicmap  | no        | yes         | String | tm-handles |         |
  // | syntax    | no        | no          | String | "XTM" (+)  | XTM     |
  // | view      | no        | no          | String | "stub" (+) | stub    |
  // -----------------------------------------------------------------------
  // (+) means other values may be allowed later.
  public static void getTopic(NavigatorApplicationIF navapp,
                              Collection items,
                              Collection subjects,
                              Collection indicators,
                              String[] tmids, 
                              String syntax,
                              String view,
                              DocumentHandler handler) throws Exception {

    // verify parameters
    if (syntax == null)
      syntax = RAPServlet.SYNTAX_XTM; // default
    if (!(syntax.equals(RAPServlet.SYNTAX_XTM) || 
          syntax.equals(RAPServlet.SYNTAX_TM_XML)))
      throw new TMRAPException("Invalid value for 'syntax' parameter: '" +
                               syntax + "'");
    if (view == null)
      view = "stub";
    if (!view.equals("stub") && !view.equals("names"))
      throw new TMRAPException("Invalid value for 'view' parameter: '" +
                               view + "'");

    // get on with it
    TopicIndexIF index = getTopicIndex(navapp, true, tmids);
    try {
      Collection topics = index.getTopics(indicators, items, subjects);
      if (syntax.equals(RAPServlet.SYNTAX_XTM))
        generateXTM(handler, topics, subjects, items, indicators, true);
      else {     
        generateTMXML(handler, topics, subjects, items, indicators,
                      view.equals("names"),
                      tmids != null && tmids.length == 1);
      }
    } finally {
      index.close();
    }
  }
  
  // ===== GET-TOLOG =======================================================
  // -----------------------------------------------------------------------
  // | Parameter | Required? | Repeatable? | Type   | Value      | Default |
  // -----------------------------------------------------------------------
  // | tolog     | yes       | no          | String | query      |         |
  // | topicmap  | yes       | no          | String | tm-handle  |         |
  // | syntax    | no        | no          | String | "XTM" (+)  | XTM     |
  // | view      | no        | no          | String | "stub" (+) | stub    |
  // -----------------------------------------------------------------------
  // (+) means other values may be allowed later.  
  public static void getTolog(NavigatorApplicationIF navapp,
                              String tolog,
                              String tmid,
                              String syntax,
                              String view,
                              DocumentHandler handler) throws Exception {

    if (tmid == null)
      throw new TMRAPException("No value given for required parameter " +
                               RAPServlet.TOPICMAP_PARAMETER_NAME);
    if (tolog == null)
      throw new TMRAPException("No value given for required parameter " +
                               RAPServlet.TOLOG_PARAMETER_NAME);
    if (syntax == null)
      syntax = RAPServlet.SYNTAX_TOLOG;
    if (!syntax.equals(RAPServlet.SYNTAX_TOLOG) &&
        !syntax.equals(RAPServlet.SYNTAX_XTM) &&
        !syntax.equals(RAPServlet.SYNTAX_TM_XML))
      throw new TMRAPException("Unsupported syntax '" + syntax + "'");
    
    TopicMapIF topicmap = navapp.getTopicMapById(tmid, true);
    try {
      QueryProcessorIF processor = QueryUtils.getQueryProcessor(topicmap);
      QueryResultIF result = processor.execute(tolog);
      if (syntax.equals(RAPServlet.SYNTAX_XTM) && result.getWidth() != 1)
        throw new TMRAPException(
          "The tolog query must produce a query result of exactly one column," +
          "but produced " + result.getWidth() + " columns.");

      if (syntax.equals(RAPServlet.SYNTAX_XTM)) {
        Collection topics = getTopics(result);
        generateXTM(handler, topics, Collections.emptyList(),
                    Collections.emptyList(),
                    Collections.emptyList(), false);
      } else if (syntax.equals(RAPServlet.SYNTAX_TM_XML)) {
        Collection topics = getTopics(result);
        generateTMXML(handler, topics, null, Collections.emptyList(),
                      Collections.emptyList(), false, false);
      } else {
        try {
          generateTolog(handler, result, view);
        } catch (SAXException e) {
          throw new TMRAPException("Unknown problem: " + e);
        }
      }
    } finally {
      // FIXME: need to close query result
      topicmap.getStore().close();
    }
  }

  // ===== DELETE-TOPIC =======================================================
  // -----------------------------------------------------------------------
  // | Parameter | Required? | Repeatable? | Type   | Value      | Default |
  // -----------------------------------------------------------------------
  // | item      | no        | yes         | URI    | locator    |         |
  // | subject   | no        | yes         | URI    | locator    |         |
  // | identifier| no        | yes         | URI    | locator    |         |
  // | topicmap  | no        | yes         | String | tm-handles |         |
  // -----------------------------------------------------------------------
  public static String deleteTopic(NavigatorApplicationIF navapp,
                                   Collection items,
                                   Collection subjects,
                                   Collection identifiers,
                                   String[] tmids) throws Exception {

    TopicIndexIF index = getTopicIndex(navapp, false, tmids);
    try {
      // The TopicIndexIF takes care of handling TM access

      // Delete the topic(s)
      Collection topics = index.getTopics(identifiers, items, subjects);
      Iterator it = topics.iterator();
      while (it.hasNext())
        it.next().remove();

      // Return a message
      return "Deleted " + topics.size() + " topics";
    } finally {
      index.close();
    }
  }

  // ===== ADD-FRAGMENT ====================================================
  // -----------------------------------------------------------------------
  // | Parameter | Required? | Repeatable? | Type   | Value      | Default |
  // -----------------------------------------------------------------------
  // | syntax    | yes       | no          | String | "XTM"/     | XTM     |
  // |           |           |             |        |   "LTM"/   |         |
  // |           |           |             |        |   "CTM"(+) |         |
  // | fragment  | yes       | no          | String |            |         |
  // | topicmap  | yes       | no          | String | tm-handles |         |
  // -----------------------------------------------------------------------
  // (+) means other values may be allowed later.
  public static void addFragment(NavigatorApplicationIF navapp,
                                 String fragment,
                                 String syntax,
                                 String tmid)
    throws NavigatorRuntimeException, IOException, TMRAPException {

    TopicMapIF topicmap = navapp.getTopicMapById(tmid, false);
    try {
      LocatorIF base = topicmap.getStore().getBaseAddress();
      StringReader reader = new StringReader(fragment);
      if (syntax.equals(RAPServlet.SYNTAX_XTM))
        new XTMTopicMapReader(reader, base).importInto(topicmap);
      else if (syntax.equals(RAPServlet.SYNTAX_LTM))
        new LTMTopicMapReader(reader, base).importInto(topicmap);
      else if (syntax.equals(RAPServlet.SYNTAX_CTM))
        new CTMTopicMapReader(reader, base).importInto(topicmap);
      else
        throw new TMRAPException("Bad syntax value: '" + syntax + "'");
      topicmap.getStore().commit();
    } finally {
      topicmap.getStore().close();
    }
  }

  // ===== UPDATE-TOPIC ====================================================
  // -----------------------------------------------------------------------
  // | Parameter | Required? | Repeatable? | Type   | Value      | Default |
  // -----------------------------------------------------------------------
  // | syntax    | yes       | no          | String | "XTM"/     | XTM     |
  // |           |           |             |        |   "LTM"(+) |         |
  // | fragment  | yes       | no          | String |            |         |
  // | topicmap  | yes       | no          | String | tm-handles |         |
  // -----------------------------------------------------------------------
  // (+) means other values may be allowed later.
  public static void updateTopic(NavigatorApplicationIF navapp,
                                 String fragment,
                                 String syntax,
                                 String tmid,
                                 Collection indicators,
                                 Collection items,
                                 Collection subjects)
    throws NavigatorRuntimeException, IOException, TMRAPException {

    TopicMapIF topicmap = navapp.getTopicMapById(tmid, false);
    try {
      LocatorIF base = topicmap.getStore().getBaseAddress();
      InMemoryTopicMapStore store = new InMemoryTopicMapStore();
      if (base != null)
        store.setBaseAddress(base);
      TopicMapIF ftm = store.getTopicMap();
      
      StringReader reader = new StringReader(fragment);
      if (syntax.equals(RAPServlet.SYNTAX_XTM))
        new XTMTopicMapReader(reader, base).importInto(ftm);
      else if (syntax.equals(RAPServlet.SYNTAX_LTM))
        new LTMTopicMapReader(reader, base).importInto(ftm);
      else
        throw new TMRAPException("Bad syntax value: '" + syntax + "'");

      // FIXME: THIS IS DIFFICULT IN PRACTICE!! NEED TO BE ABLE TO SPECIFY
      // RELATIVE ITEM IDENTIFIERS, METHINKS...
      
      TopicMapTopicIndex ix = new TopicMapTopicIndex(ftm, null, null, null);
      Collection topics = ix.getTopics(indicators, items, subjects);
      if (topics.size() != 1)
        throw new TMRAPException("Wrong number of topics identified in fragment");
      TopicIF ft = topics.iterator().next();
      TopicMapSynchronizer.update(topicmap, ft);
      
      topicmap.getStore().commit();
    } finally {
      topicmap.getStore().close();
    }
  }
  
  // ===== GET-TOPIC-PAGE ===================================================
  // ------------------------------------------------------------------------
  // | Parameter  | Required? | Repeatable? | Type   | Value      | Default |
  // ------------------------------------------------------------------------
  // | source     |           | yes         | URI    | locator    |         |
  // | subject    |           | yes         | URI    | locator    |         |
  // | identifier |           | yes         | URI    | locator    |         |
  // | topicmap   | no        | yes         | String | tm-handles |         |
  // | syntax     | no        | no          | String | "XTM" (+)  | XTM     |
  // ------------------------------------------------------------------------
  // (+) means other values may be allowed later.
  public static TopicMapIF getTopicPage(NavigatorApplicationIF navapp,
                                        TMRAPConfiguration config,
                                        Collection items,
                                        Collection subjects,
                                        Collection indicators,
                                        String[] tmids)
    throws IOException, NavigatorRuntimeException {
    
    TopicIndexIF index = getTopicIndex(navapp, true, tmids,
                                       config.getViewURI(),
                                       config.getEditURI());
    try {
      // Create a new topic map.
      TopicMapStoreIF store = new InMemoryTopicMapStore();
      TopicMapIF topicmap = store.getTopicMap();
      
      // Get builder that adds new TAOs to the topic map.
      TopicMapBuilderIF builder = topicmap.getBuilder();
  
      // Create topic type rap:server.
      TopicIF rapServerType = makeTopic(topicmap, "server");
      
      // Create topic of type rap:server to represent the server.
      TopicIF rapServerTopic = builder.makeTopic(rapServerType);
      if (config.getServerName() != null)
        builder.makeTopicName(rapServerTopic, config.getServerName());
      TopicPages pages = index.getTopicPages2(indicators, items, subjects);
      
      // If there are any matching topics:
      if (!pages.getTopicMapHandles().isEmpty()) {
        // Create a topic to represent the matching topics.
        TopicIF matchTopic = builder.makeTopic();
        
        if (pages.getName() != null)
          builder.makeTopicName(matchTopic, pages.getName());
        
        Iterator it = pages.getItemIdentifiers().iterator();
        while (it.hasNext())
          matchTopic.addItemIdentifier(it.next());
        
        it = pages.getSubjectIdentifiers().iterator();
        while (it.hasNext())
          matchTopic.addSubjectIdentifier(it.next());
        
        it = pages.getSubjectLocators().iterator();
        if (it.hasNext())
          matchTopic.addSubjectLocator(it.next());
        
        // Create topic type rap:topicmap.
        TopicIF rapTopicMapType = makeTopic(topicmap, "topicmap");
  
        // Create occurrence type rap:handle.
        TopicIF rapHandleType = makeTopic(topicmap, "handle");
  
        // Create association type rap:contained-in.
        TopicIF rapContainedInType = makeTopic(topicmap, "contained-in");
  
        // Create association role type rap:container.
        TopicIF rapContainerType = makeTopic(topicmap, "container");
  
        // Create association role type rap:container.
        TopicIF rapContaineeType = makeTopic(topicmap, "containee");
  
        // For each topic map that has at least one matching topic.
        Iterator topicmapHandlesIt = pages.getTopicMapHandles().iterator();
        while (topicmapHandlesIt.hasNext()) {
          String currentHandle = topicmapHandlesIt.next();
          
          TopicIF currentRAPTopicMap = builder.makeTopic(rapTopicMapType);
          
          // Add topic map handle occurrence to identify topic.
          builder.makeOccurrence(currentRAPTopicMap, rapHandleType,
              currentHandle);
          
          String tmName = pages.getTMName(currentHandle);
          if (tmName != null)
            builder.makeTopicName(currentRAPTopicMap, tmName);
          
          // This topic map is contained in the server it came from.
          AssociationIF tmInServerAssoc = builder.makeAssociation(rapContainedInType);
          builder.makeAssociationRole(tmInServerAssoc, rapContaineeType, currentRAPTopicMap);
          builder.makeAssociationRole(tmInServerAssoc, rapContainerType, rapServerTopic);
  
          TopicIF rapEditPageType = null;
          if (config.getEditURI() != null)
            // Create topic type rap:edit-page
            rapEditPageType = makeTopic(topicmap, "edit-page");
  
          TopicIF rapViewPageType = null;
          if (config.getViewURI() != null)
            // Create topic type rap:view-page
            rapViewPageType = makeTopic(topicmap, "view-page");
          
          // For each page. Each page object represents both view and edit page
          // when there's both.
          Iterator pagesIt = pages.getPages(currentHandle).iterator();
          while (pagesIt.hasNext()) {
            TopicPage currentPage = pagesIt.next();
            
            String editURL = currentPage.getEditURL();
            String viewURL = currentPage.getViewURL();
            
            if (editURL != null) {
              LocatorIF editSubject = new URILocator(currentPage.getEditURL());
              if (topicmap.getTopicBySubjectLocator(editSubject) == null) {
                // Create a topic to to represent the current edit-page.
                TopicIF currentEditPageTopic = builder.makeTopic(rapEditPageType);
                
                // Set the subject locator to be the link text
                currentEditPageTopic.addSubjectLocator(editSubject);
                
                AssociationIF association = builder.makeAssociation(rapContainedInType);
                builder.makeAssociationRole(association, rapContaineeType, currentEditPageTopic);
                builder.makeAssociationRole(association, rapContainerType, currentRAPTopicMap);
              }
            }
            
            if (viewURL != null) {
              LocatorIF viewSubject = new URILocator(currentPage.getViewURL());
              if (topicmap.getTopicBySubjectLocator(viewSubject) == null) {
                // Create a topic to represent the current view-page.
                TopicIF currentViewPageTopic = builder.makeTopic(rapViewPageType);
                
                // Set the subject locator to be the link text
                currentViewPageTopic.addSubjectLocator(viewSubject);
                
                AssociationIF association = builder.makeAssociation(rapContainedInType);
                builder.makeAssociationRole(association, rapContaineeType, currentViewPageTopic);
                builder.makeAssociationRole(association, rapContainerType, currentRAPTopicMap);
              }
            }
          }
        }
      }

      return topicmap;
    } finally {
      index.close();
    }
  }

  // ===== TOLOG-UPDATE =====================================================
  // ------------------------------------------------------------------------
  // | Parameter  | Required? | Repeatable? | Type   | Value      | Default |
  // ------------------------------------------------------------------------
  // | topicmap   | yes       | no          | String | tm-handles |         |
  // | tolog      | yes       | no          | String | query      |         |
  // ------------------------------------------------------------------------
  public static int tologUpdate(NavigatorApplicationIF navapp,
                                String tmid,
                                String statement)
    throws IOException, NavigatorRuntimeException, InvalidQueryException {
    TopicMapIF topicmap = navapp.getTopicMapById(tmid, false);
    try {
      QueryProcessorIF processor = QueryUtils.getQueryProcessor(topicmap);
      int rows = processor.update(statement);
      topicmap.getStore().commit();
      return rows;
    } finally {
      topicmap.getStore().close();
    }
  }
  
  // --- Internal helpers

  private static TopicIF makeTopic(TopicMapIF tm, String id)
    throws MalformedURLException {
    TopicMapBuilderIF builder = tm.getBuilder();
    TopicIF topic = builder.makeTopic();
    topic.addSubjectIdentifier(new URILocator(RAPServlet.RAP_NAMESPACE + id));
    return topic;
  }
    
  private static TopicIndexIF getTopicIndex(NavigatorApplicationIF navapp, boolean readonly,
                                            String[] tmids)
    throws NavigatorRuntimeException {
    return getTopicIndex(navapp, readonly, tmids, null, null);
  }

  private static TopicIndexIF getTopicIndex(NavigatorApplicationIF navapp,
                                            boolean readonly,
                                            String[] tmids,
                                            String viewBaseuri,
                                            String editBaseuri)
    throws NavigatorRuntimeException {    
    if (tmids == null || tmids.length == 0)
      return new RegistryTopicIndex(navapp.getTopicMapRepository(), readonly,
                                    editBaseuri, viewBaseuri);

    List topicIndexes = new ArrayList();
    for (int i = 0; i < tmids.length; i++) {
      TopicMapIF topicmap = navapp.getTopicMapById(tmids[i], readonly);
      TopicIndexIF currentIndex =
        new TopicMapTopicIndex(topicmap, editBaseuri, viewBaseuri, tmids[i]);
      topicIndexes.add(currentIndex);
    }
    return new FederatedTopicIndex(topicIndexes);
  }

  private static void generateXTM(DocumentHandler ser, Collection topics,
                                  Collection subjectLocators,
                                  Collection srclocs,
                                  Collection indicators, boolean merge)
    throws SAXException {
        
    AttributeListImpl atts = new AttributeListImpl();
    atts.addAttribute("xmlns", "CDATA", "http://www.topicmaps.org/xtm/1.0/");
    atts.addAttribute("xmlns:xlink", "CDATA", "http://www.w3.org/1999/xlink");
    
    ser.startDocument();
        
    // Output element
    ser.startElement("topicMap", atts);

    // serialize as XTM Fragment 
    XTMFragmentExporter fragExporter = new XTMFragmentExporter();

    fragExporter.exportTopics(topics.iterator(), ser);
        
    // add binding topic if more than one result.
    if (merge && topics.size() > 1) {
      atts.clear();
      // id must be unique on this server for all time
      String suffix =
        new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) +
        (counter++);
      atts.addAttribute("id", "CDATA", "unifying-topic-" + suffix);
      ser.startElement("topic", atts);

      atts.clear();
      ser.startElement("subjectIdentity", atts);

      Iterator sublociter = subjectLocators.iterator();
      while (sublociter.hasNext()) {
        LocatorIF uriloc = sublociter.next();            
        atts.clear();
        atts.addAttribute("xlink:href", "CDATA", uriloc.getExternalForm());
        ser.startElement("resourceRef", atts);  
        ser.endElement("resourceRef");
      }
                              
      Iterator srclociter = srclocs.iterator();
      while (srclociter.hasNext()) {
        LocatorIF uriloc = srclociter.next();            
        atts.clear();
        atts.addAttribute("xlink:href", "CDATA", uriloc.getExternalForm());         
        ser.startElement("topicRef", atts);   
        ser.endElement("topicRef");
      }
      
      Iterator indicatoriter = indicators.iterator();
      while (indicatoriter.hasNext()) {
        LocatorIF uriloc = indicatoriter.next();            
        atts.clear();
        atts.addAttribute("xlink:href", "CDATA", uriloc.getExternalForm());
        ser.startElement("subjectIndicatorRef", atts); 
        ser.endElement("subjectIndicatorRef");                  
      }
      ser.endElement("subjectIdentity");
      ser.endElement("topic");    
    }
        
    // output XTM close
    ser.endElement("topicMap");    
    ser.endDocument();
  }

  private static void generateTMXML(DocumentHandler ser, Collection topics,
                                    Collection subjectLocators,
                                    Collection srclocs,
                                    Collection indicators,
                                    boolean names,
                                    boolean singletm)
    throws SAXException {
    try {
      TMXMLWriter writer = new TMXMLWriter(ser);
      writer.gatherPrefixes(topics);
      writer.startTopicMap(null); // no topic map for now. this means that we don't get topic map reificaiton
      writer.writeTopics(topics);
      
      // FIXME: add binding topic here if multiple
      
      if (names) {
      // also output names of referenced topics
        InMemoryTopicMapStore store = new InMemoryTopicMapStore();
        TopicMapIF tm = store.getTopicMap();
        Iterator it = topics.iterator();
        while (it.hasNext()) {
          TopicIF topic = it.next();
          copyReferencedTopics(tm, topic);
        }
        
        // yes, I know this isn't pretty; sorry. I'll work this out
        // when I have time. I need this for the TMRAP tutorial :-(
        if (singletm) {
          // ie: it's all from the same topic map, *therefore* we can
          // set the base locator of the new TM, and ensure that the ID
          // references from the fragment already created match the IDs
          // in the fragment we are about to create.
          TopicIF atopic = topics.iterator().next();
          LocatorIF base = atopic.getTopicMap().getStore().getBaseAddress();
          store.setBaseAddress(base);
        }
        
        writer.writeTopics(tm.getTopics());
      }
    
      writer.endTopicMap();
      writer.close();
    } catch (IOException e) {
      throw new OntopiaRuntimeException(e);
    }
  }

  private static void copyReferencedTopics(TopicMapIF tm, TopicIF source) {
    Iterator it = source.getRoles().iterator();
    while (it.hasNext()) {
      AssociationRoleIF role = it.next();
      AssociationIF assoc = role.getAssociation();
      Iterator it2 = assoc.getRoles().iterator();
      while (it2.hasNext()) {
        AssociationRoleIF other = it2.next();
        TopicIF refd = other.getPlayer();
        if (refd != source)
          copyTopic(tm, refd);
      }
    }
  }

  private static void copyTopic(TopicMapIF tm, TopicIF referenced) {
    TopicMapBuilderIF builder = tm.getBuilder();
    TopicIF topic = builder.makeTopic();

    // copy identity
    Iterator it = referenced.getSubjectLocators().iterator();
    while (it.hasNext())
      topic.addSubjectLocator(it.next());
    it = referenced.getSubjectIdentifiers().iterator();
    while (it.hasNext())
      topic.addSubjectIdentifier(it.next());
    it = referenced.getItemIdentifiers().iterator();
    while (it.hasNext())
      topic.addItemIdentifier(it.next());
    
    // copy name
    builder.makeTopicName(topic, TopicStringifiers.toString(referenced));
  }

  private static Collection getTopics(QueryResultIF result)
    throws TMRAPException {
    Collection topics = new ArrayList();
    while (result.next()) {
      Object current = result.getValue(0);
      if (!(current instanceof TopicIF))
        throw new TMRAPException(
          "The query result produced by the tolog query must only contain " +
          "topics, but contained an object of type " + 
          current.getClass().getName() + ".");
      
      topics.add((TopicIF) current);
    }
    return topics;
  }
  
  private static void generateTolog(DocumentHandler handler,
                                    QueryResultIF result,
                                    String view)
    throws SAXException, TMRAPException {
    AttributeListImpl atts = new AttributeListImpl();
    handler.startDocument();

    atts.addAttribute("xmlns:x", "CDATA", "http://www.topicmaps.org/xtm/1.0/");
    atts.addAttribute("xmlns:l", "CDATA", "http://www.w3.org/1999/xlink");
    handler.startElement("result", atts);

    atts.clear();
    handler.startElement("head", atts);
    for (int ix = 0; ix < result.getWidth(); ix++) {
      handler.startElement("column", atts);
      String name = result.getColumnName(ix);
      char[] chars = name.toCharArray();
      handler.characters(chars, 0, chars.length);      
      handler.endElement("column");
    }
    handler.endElement("head");

    while (result.next()) {
      handler.startElement("row", atts);
      for (int ix = 0; ix < result.getWidth(); ix++) {
        handler.startElement("value", atts);
        Object value = result.getValue(ix);
        if (value == null) {
          // use empty element
        } else if (value instanceof TopicIF) {
          TopicIF topic = (TopicIF) value;
          if (view == null)
            view = "stub";
          if (view.equals("stub"))
            makeStub(topic, handler);
          else if (view.equals("full-name"))
            makeFullName(topic, handler);
        } else if (value instanceof String || value instanceof Number) {
          String svalue = value.toString();
          char[] chars = svalue.toCharArray();
          handler.characters(chars, 0, chars.length);
        } else
          throw new TMRAPException("Unsupported value: " + value);
        
        handler.endElement("value");
      }
      handler.endElement("row");
    }
    
    handler.endElement("result");
    handler.endDocument();
  }

  public static void makeStub(TopicIF topic, DocumentHandler handler)
    throws TMRAPException, SAXException {
    AttributeListImpl atts = new AttributeListImpl(); // this is slow!!
    LocatorIF loc;
    String elemname;
    if (!topic.getSubjectLocators().isEmpty()) {
      loc = topic.getSubjectLocators().iterator().next();
      elemname = "x:resourceRef";
    } else if (!topic.getSubjectIdentifiers().isEmpty()) {
      loc = topic.getSubjectIdentifiers().iterator().next();
      elemname = "x:subjectIndicatorRef";
    } else if (!topic.getItemIdentifiers().isEmpty()) {
      loc = topic.getItemIdentifiers().iterator().next();
      elemname = "x:topicRef";
    } else
      throw new TMRAPException("Not implemented yet"); // FIXME!!

    atts.addAttribute("l:href", "CDATA", loc.getExternalForm());
    handler.startElement(elemname, atts);
    handler.endElement(elemname);
  }

  public static void makeFullName(TopicIF topic, DocumentHandler handler)
    throws SAXException {
    generateTMXML(handler, Collections.singleton(topic),
                  null, Collections.emptyList(),
                  Collections.emptyList(), false, false);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy