<commit_before>
// $Id: TopicMap.java,v 1.11 2009/04/30 09:53:42 geir.gronmo Exp $

package net.ontopia.topicmaps.nav2.webapps.ontopoly.model;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.infoset.impl.basic.URILocator;
import net.ontopia.topicmaps.core.OccurrenceIF;
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.entry.TopicMapReferenceIF;
import net.ontopia.topicmaps.nav2.webapps.ontopoly.sysmodel.OntopolyRepository;
import net.ontopia.topicmaps.nav2.webapps.ontopoly.sysmodel.TopicMapReference;
import net.ontopia.topicmaps.nav2.webapps.ontopoly.utils.OntopolyModelUtils;
import net.ontopia.topicmaps.query.core.DeclarationContextIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
import net.ontopia.topicmaps.query.core.QueryProcessorIF;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import net.ontopia.topicmaps.query.utils.QueryUtils;
import net.ontopia.topicmaps.query.utils.RowMapperIF;
import net.ontopia.topicmaps.utils.MergeUtils;
import net.ontopia.topicmaps.utils.TopicStringifiers;
import net.ontopia.topicmaps.xml.XTMTopicMapReference;
import net.ontopia.utils.CollectionUtils;
import net.ontopia.utils.OntopiaRuntimeException;

/**
 * INTERNAL: Represents an Ontopoly topic map.
 */
public class TopicMap {

  static final String ON = "http://psi.ontopia.net/ontology/";
  static final String XTM = "http://www.topicmaps.org/xtm/1.0/core.xtm#";
  static final String TEST = "http://psi.example.org/test/";
  static final String TECH = "http://www.techquila.com/psi/hierarchy/#";
  static final String DC = "http://purl.org/dc/elements/1.1/";
  static final String XSD = "http://www.w3.org/2001/XMLSchema#";

  private static final String declarations = 
    "using xtm for i\"" + XTM + "\" "
    + "using on for i\"" + ON + "\" " 
    + "using test for i\"" + TEST + "\" "
    + "using tech for i\"" + TECH + "\" " 
    + "using dc for i\"" + DC + "\" ";
  
  private OntopolyRepository repository;

  private TopicMapIF topicMapIF;
  private DeclarationContextIF dc;
  
  private String topicMapId;
  private QueryProcessorIF qp;

  public TopicMap(TopicMapReference topicMapReference) {
    this.repository = topicMapReference.getRepository();
    this.topicMapId = topicMapReference.getId();
    try {
      this.topicMapIF = repository.getTopicMapRepository().getReferenceByKey(
          topicMapId).createStore(false).getTopicMap();
    } catch (Exception e) {
      throw new OntopiaRuntimeException(e);
    }

    // initialize query context
    initQueryContext();
  }

  public TopicMap(OntopolyRepository repository, TopicMapIF topicMapIF,
      String topicMapId) {
    this.repository = repository;
    this.topicMapIF = topicMapIF;
    this.topicMapId = topicMapId;

    // initialize query wrapper
    initQueryContext();
  }

  private void initQueryContext() {
    try {
      this.qp = QueryUtils.getQueryProcessor(topicMapIF);
      // load built-in declarations
      this.dc = QueryUtils.parseDeclarations(topicMapIF, declarations);

      // load custom declarations
      TopicIF typeIf = OntopolyModelUtils.getTopicIF(this, PSI.ON, "tolog-declarations", false);
      if (typeIf != null) {
        TopicIF topicIf = getTopicMapIF().getReifier();
        if (topicIf != null) {
          OccurrenceIF occ = OntopolyModelUtils.findOccurrence(typeIf, topicIf);
          if (occ != null)
            this.dc = QueryUtils.parseDeclarations(topicMapIF, declarations, this.dc);
        }
      }
    } catch (InvalidQueryException e) {
      throw new OntopiaRuntimeException(e);
    }
  }

  public DeclarationContextIF getDeclarationContext() {
    return dc;
  }

  public QueryProcessorIF getQueryProcessor() {
    return qp;
  }
  
  public boolean containsOntology() {
    return (getTopicMapIF().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_VERSION) != null);
  }

  public boolean isDeleteable() {
    return getTopicMapIF().getStore().getReference().getSource()
        .supportsDelete();
  }

  public TopicMapIF getTopicMapIF() {
    return topicMapIF;
  }

  public <T> QueryMapper<T> newQueryMapperNoWrap() {
    return new QueryMapper<T>(getQueryProcessor(), getDeclarationContext());
  }

  public <T> QueryMapper<T> newQueryMapper(final Class<T> type) {
    return new QueryMapper<T>(getQueryProcessor(), getDeclarationContext()) {
      @SuppressWarnings("unchecked")
      @Override
      protected T wrapValue(Object value) {
        // don't wrap if type is null
        if (type == null) return (T)value;
        
        // if (value == null) return null;
        try {      
          Constructor<T> constructor = getConstructor();
          if (constructor == null) {
            throw new OntopolyModelRuntimeException("Couldn't find constructor for the class: " + type);
          }
          return constructor.newInstance(new Object[] { value, TopicMap.this });
        } catch (Exception e) {
          throw new OntopolyModelRuntimeException(e);
        }
      }
      
      private Constructor<T> getConstructor() throws SecurityException, NoSuchMethodException {
        return type.getConstructor(TopicIF.class, TopicMap.class); 
      }
    };
  }

  public OntopolyRepository getOntopolyRepository() {
    return repository;
  }

  public TopicIF getTopicIFById(String id) {
    // look up topic by object id or subject identifier
    TopicIF topic = (TopicIF)getTopicMapIF().getObjectById(id);
    if (topic == null) {
      try {
        return topicMapIF.getTopicBySubjectIdentifier(URILocator.create(id));
      } catch (Exception e) {
        return null;
      }
    }
    return topic;
  }

  public Topic getTopicById(String id) {
    TopicIF topic = getTopicIFById(id);
    return new Topic(topic, this);
  }

  public Topic getReifier() {
    return new Topic(makeReifier(), this);
  }
  
  protected TopicIF makeReifier() {
    TopicIF reifier = getTopicMapIF().getReifier();
    if (reifier == null) {
      // IMPORTANT: check old-style reification
      TopicMapIF tm = getTopicMapIF();
      Iterator iter = tm.getItemIdentifiers().iterator();
      while (iter.hasNext()) {
        LocatorIF srcloc = (LocatorIF) iter.next();
        TopicIF _reifier = tm.getTopicBySubjectIdentifier(srcloc);
        if (_reifier != null) {
          if (reifier != null)
            MergeUtils.mergeInto(reifier, _reifier);
          else
            reifier = _reifier;
        }
      }
      if (reifier == null)
        reifier = tm.getBuilder().makeTopic();
      tm.setReifier(reifier);
      reifier.addType(tm.getTopicBySubjectIdentifier(PSI.ON_TOPIC_MAP));
    }
    return reifier;
  }

  /**
   * Returns the name of the topic map, or null if it has none.
   */
  public String getName() {
    TopicIF reifier = getTopicMapIF().getReifier();
    return reifier == null ? null : TopicStringifiers.toString(reifier);
  }

  /**
   * Sets the name of the topic map, creating a new base name object if
   * necessary. Also updates the name in the system topic map and saves that
   * topic map.
   */
  public void setName(String _value) {
    String value = (_value == null ? "" : _value);
    TopicIF topic = makeReifier();
    // update existing new name or create a new one
    Collection names = OntopolyModelUtils.findTopicNames(null, topic, value,
                                                         Collections.EMPTY_SET);
    Iterator iter = names.iterator();
    if (iter.hasNext()) {
      TopicNameIF bn = (TopicNameIF) iter.next();
      bn.setValue(value);
    } else
      getTopicMapIF().getBuilder().makeTopicName(topic, value);

    // remove superflous names
    while (iter.hasNext())
      ((TopicNameIF) iter.next()).remove();
    // update ontopoly repository if there is one
    if (repository != null) {
      TopicMapReference ref = repository.getReference(topicMapId);
      ref.setName(value);
    }
  }

  /**
   * Returns the version of the Ontopoly meta-ontology used in this topic map.
   */
  public float getOntologyVersion() {
    TopicIF reifier = getTopicMapIF().getReifier();
    if (reifier == null) return 0;
    TopicIF ontologyVersion = getTopicMapIF().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_VERSION);
    Collection occs = OntopolyModelUtils.findOccurrences(ontologyVersion,
        reifier, Collections.EMPTY_SET);
    if (occs.isEmpty())
      return 0;
 
    String versionNumber = ((OccurrenceIF) occs.iterator().next()).getValue();
    try {
      return Float.parseFloat(versionNumber);
    } catch (NumberFormatException e) {
      return 0;
    }
  }

  /**
   * INTERNAL: Sets the version of the Ontopoly meta-ontology used in this topic map. Used by code that performs upgrades.
   */
  public void setOntologyVersion(float value) {
    String versionNumber = Float.toString(value);
    TopicIF reifier = makeReifier();
    TopicIF ontologyVersion = getTopicMapIF().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_VERSION);
    Collection occs = OntopolyModelUtils.findOccurrences(ontologyVersion,
        reifier, Collections.EMPTY_SET);
    Iterator iter = occs.iterator();
    if (iter.hasNext()) {
      OccurrenceIF occ = (OccurrenceIF) iter.next();
      occ.setValue(versionNumber);
    } else {
      getTopicMapIF().getBuilder().makeOccurrence(reifier, ontologyVersion, versionNumber);
    }
    // remove superflous occurrences
    while (iter.hasNext())
      ((OccurrenceIF) iter.next()).remove();
  }

  /**
   * Returns the Id of the topic map reference in the topic map registry.
   */
  public String getId() {
    return topicMapId;
  }

//  /* get Types */
//
//  public List getSystemTopicTypes() {
//    String query = "select $type from "
//        + "{ instance-of($type, on:ontology-type) | $type = on:ontology-type }?";
//
//    List result = getQueryWrapper().queryForList(query,
//        OntopolyModelUtils.getRowMapperOneColumn());
//
//    return castResultObjects(result, TopicType.class);
//  }

  /**
   * Returns a list of the topic types that is not a system topic type.
   */
  public List<TopicType> getTopicTypes() {
    String query = "select $type from "
      + "instance-of($type, on:topic-type)"
      //! + ", not (instance-of($type, on:ontology-type)), $type /= on:ontology-type"
      + " order by $type?";

    QueryMapper<TopicType> qm = newQueryMapper(TopicType.class);    
    return qm.queryForList(query);
  }

  /**
   * Returns a list of the topic types that is not a system topic type.
   */
  public List<TopicType> getTopicTypesWithLargeInstanceSets() {
    String query = "select $type from on:has-large-instance-set($type : on:topic-type)?";

    QueryMapper<TopicType> qm = newQueryMapper(TopicType.class);
    return qm.queryForList(query);
  }

  public List<OccurrenceType> getOccurrenceTypes() {
    String query = "select $type from "<commit_msg>Style fixes. Removed commented-out code.<commit_after>
// $Id: TopicMap.java,v 1.11 2009/04/30 09:53:42 geir.gronmo Exp $

package net.ontopia.topicmaps.nav2.webapps.ontopoly.model;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.infoset.impl.basic.URILocator;
import net.ontopia.topicmaps.core.OccurrenceIF;
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.entry.TopicMapReferenceIF;
import net.ontopia.topicmaps.nav2.webapps.ontopoly.sysmodel.OntopolyRepository;
import net.ontopia.topicmaps.nav2.webapps.ontopoly.sysmodel.TopicMapReference;
import net.ontopia.topicmaps.nav2.webapps.ontopoly.utils.OntopolyModelUtils;
import net.ontopia.topicmaps.query.core.DeclarationContextIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
import net.ontopia.topicmaps.query.core.QueryProcessorIF;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import net.ontopia.topicmaps.query.utils.QueryUtils;
import net.ontopia.topicmaps.query.utils.RowMapperIF;
import net.ontopia.topicmaps.utils.MergeUtils;
import net.ontopia.topicmaps.utils.TopicStringifiers;
import net.ontopia.topicmaps.xml.XTMTopicMapReference;
import net.ontopia.utils.CollectionUtils;
import net.ontopia.utils.OntopiaRuntimeException;

/**
 * INTERNAL: Represents an Ontopoly topic map.
 */
public class TopicMap {

  static final String ON = "http://psi.ontopia.net/ontology/";
  static final String XTM = "http://www.topicmaps.org/xtm/1.0/core.xtm#";
  static final String TEST = "http://psi.example.org/test/";
  static final String TECH = "http://www.techquila.com/psi/hierarchy/#";
  static final String DC = "http://purl.org/dc/elements/1.1/";
  static final String XSD = "http://www.w3.org/2001/XMLSchema#";

  private static final String declarations = 
    "using xtm for i\"" + XTM + "\" "
    + "using on for i\"" + ON + "\" " 
    + "using test for i\"" + TEST + "\" "
    + "using tech for i\"" + TECH + "\" " 
    + "using dc for i\"" + DC + "\" ";
  
  private OntopolyRepository repository;

  private TopicMapIF topicMapIF;
  private DeclarationContextIF dc;
  
  private String topicMapId;
  private QueryProcessorIF qp;

  public TopicMap(TopicMapReference topicMapReference) {
    this.repository = topicMapReference.getRepository();
    this.topicMapId = topicMapReference.getId();
    try {
      this.topicMapIF = repository.getTopicMapRepository().getReferenceByKey(
          topicMapId).createStore(false).getTopicMap();
    } catch (Exception e) {
      throw new OntopiaRuntimeException(e);
    }

    // initialize query context
    initQueryContext();
  }

  public TopicMap(OntopolyRepository repository, TopicMapIF topicMapIF,
      String topicMapId) {
    this.repository = repository;
    this.topicMapIF = topicMapIF;
    this.topicMapId = topicMapId;

    // initialize query wrapper
    initQueryContext();
  }

  private void initQueryContext() {
    try {
      this.qp = QueryUtils.getQueryProcessor(topicMapIF);
      // load built-in declarations
      this.dc = QueryUtils.parseDeclarations(topicMapIF, declarations);

      // load custom declarations
      TopicIF typeIf = OntopolyModelUtils.getTopicIF(this, PSI.ON, "tolog-declarations", false);
      if (typeIf != null) {
        TopicIF topicIf = getTopicMapIF().getReifier();
        if (topicIf != null) {
          OccurrenceIF occ = OntopolyModelUtils.findOccurrence(typeIf, topicIf);
          if (occ != null)
            this.dc = QueryUtils.parseDeclarations(topicMapIF, declarations, this.dc);
        }
      }
    } catch (InvalidQueryException e) {
      throw new OntopiaRuntimeException(e);
    }
  }

  public DeclarationContextIF getDeclarationContext() {
    return dc;
  }

  public QueryProcessorIF getQueryProcessor() {
    return qp;
  }
  
  public boolean containsOntology() {
    return (getTopicMapIF().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_VERSION) != null);
  }

  public boolean isDeleteable() {
    return getTopicMapIF().getStore().getReference().getSource()
        .supportsDelete();
  }

  public TopicMapIF getTopicMapIF() {
    return topicMapIF;
  }

  public <T> QueryMapper<T> newQueryMapperNoWrap() {
    return new QueryMapper<T>(getQueryProcessor(), getDeclarationContext());
  }

  public <T> QueryMapper<T> newQueryMapper(final Class<T> type) {
    return new QueryMapper<T>(getQueryProcessor(), getDeclarationContext()) {
      @SuppressWarnings("unchecked")
      @Override
      protected T wrapValue(Object value) {
        // don't wrap if type is null
        if (type == null) return (T)value;
        
        // if (value == null) return null;
        try {      
          Constructor<T> constructor = getConstructor();
          if (constructor == null) {
            throw new OntopolyModelRuntimeException("Couldn't find constructor for the class: " + type);
          }
          return constructor.newInstance(new Object[] { value, TopicMap.this });
        } catch (Exception e) {
          throw new OntopolyModelRuntimeException(e);
        }
      }
      
      private Constructor<T> getConstructor() throws SecurityException, NoSuchMethodException {
        return type.getConstructor(TopicIF.class, TopicMap.class); 
      }
    };
  }

  public OntopolyRepository getOntopolyRepository() {
    return repository;
  }

  public TopicIF getTopicIFById(String id) {
    // look up topic by object id or subject identifier
    TopicIF topic = (TopicIF)getTopicMapIF().getObjectById(id);
    if (topic == null) {
      try {
        return topicMapIF.getTopicBySubjectIdentifier(URILocator.create(id));
      } catch (Exception e) {
        return null;
      }
    }
    return topic;
  }

  public Topic getTopicById(String id) {
    TopicIF topic = getTopicIFById(id);
    return new Topic(topic, this);
  }

  public Topic getReifier() {
    return new Topic(makeReifier(), this);
  }
  
  protected TopicIF makeReifier() {
    TopicIF reifier = getTopicMapIF().getReifier();
    if (reifier == null) {
      // IMPORTANT: check old-style reification
      TopicMapIF tm = getTopicMapIF();
      Iterator iter = tm.getItemIdentifiers().iterator();
      while (iter.hasNext()) {
        LocatorIF srcloc = (LocatorIF) iter.next();
        TopicIF _reifier = tm.getTopicBySubjectIdentifier(srcloc);
        if (_reifier != null) {
          if (reifier != null)
            MergeUtils.mergeInto(reifier, _reifier);
          else
            reifier = _reifier;
        }
      }
      if (reifier == null)
        reifier = tm.getBuilder().makeTopic();
      tm.setReifier(reifier);
      reifier.addType(tm.getTopicBySubjectIdentifier(PSI.ON_TOPIC_MAP));
    }
    return reifier;
  }

  /**
   * Returns the name of the topic map, or null if it has none.
   */
  public String getName() {
    TopicIF reifier = getTopicMapIF().getReifier();
    return reifier == null ? null : TopicStringifiers.toString(reifier);
  }

  /**
   * Sets the name of the topic map, creating a new base name object if
   * necessary. Also updates the name in the system topic map and saves that
   * topic map.
   */
  public void setName(String _value) {
    String value = (_value == null ? "" : _value);
    TopicIF topic = makeReifier();
    // update existing new name or create a new one
    Collection names = OntopolyModelUtils.findTopicNames(null, topic, value,
                                                         Collections.EMPTY_SET);
    Iterator iter = names.iterator();
    if (iter.hasNext()) {
      TopicNameIF bn = (TopicNameIF) iter.next();
      bn.setValue(value);
    } else
      getTopicMapIF().getBuilder().makeTopicName(topic, value);

    // remove superflous names
    while (iter.hasNext())
      ((TopicNameIF) iter.next()).remove();
    // update ontopoly repository if there is one
    if (repository != null) {
      TopicMapReference ref = repository.getReference(topicMapId);
      ref.setName(value);
    }
  }

  /**
   * Returns the version of the Ontopoly meta-ontology used in this topic map.
   */
  public float getOntologyVersion() {
    TopicIF reifier = getTopicMapIF().getReifier();
    if (reifier == null) return 0;
    TopicIF ontologyVersion = getTopicMapIF().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_VERSION);
    Collection occs = OntopolyModelUtils.findOccurrences(ontologyVersion,
        reifier, Collections.EMPTY_SET);
    if (occs.isEmpty())
      return 0;
 
    String versionNumber = ((OccurrenceIF) occs.iterator().next()).getValue();
    try {
      return Float.parseFloat(versionNumber);
    } catch (NumberFormatException e) {
      return 0;
    }
  }

  /**
   * INTERNAL: Sets the version of the Ontopoly meta-ontology used in
   * this topic map. Used by code that performs upgrades.
   */
  public void setOntologyVersion(float value) {
    String versionNumber = Float.toString(value);
    TopicIF reifier = makeReifier();
    TopicIF ontologyVersion = getTopicMapIF().getTopicBySubjectIdentifier(PSI.ON_ONTOLOGY_VERSION);
    Collection occs = OntopolyModelUtils.findOccurrences(ontologyVersion,
        reifier, Collections.EMPTY_SET);
    Iterator iter = occs.iterator();
    if (iter.hasNext()) {
      OccurrenceIF occ = (OccurrenceIF) iter.next();
      occ.setValue(versionNumber);
    } else {
      getTopicMapIF().getBuilder().makeOccurrence(reifier, ontologyVersion, versionNumber);
    }
    // remove superflous occurrences
    while (iter.hasNext())
      ((OccurrenceIF) iter.next()).remove();
  }

  /**
   * Returns the Id of the topic map reference in the topic map registry.
   */
  public String getId() {
    return topicMapId;
  }

  /**
   * Returns a list of the topic types that is not a system topic type.
   */
  public List<TopicType> getTopicTypes() {
    String query = "select $type from "
      + "instance-of($type, on:topic-type)"
      //! + ", not (instance-of($type, on:ontology-type)), $type /= on:ontology-type"
      + " order by $type?";

    QueryMapper<TopicType> qm = newQueryMapper(TopicType.class);    
    return qm.queryForList(query);
  }

  /**
   * Returns a list of the topic types that is not a system topic type.
   */
  public List<TopicType> getTopicTypesWithLargeInstanceSets() {
    String query = "select $type from on:has-large-instance-set($type : on:topic-type)?";

    QueryMapper<TopicType> qm = newQueryMapper(TopicType.class);
    return qm.queryForList(query);
  }

  public List<OccurrenceType> getOccurrenceTypes() {
    String query = "select $type from "
{'fraction_non_alphanumeric': 0.11367536807876434, 'fraction_numerical': 0.002980760545569506, 'mean_word_length': 3.2297994269340973, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4159923', 'n_tokens_mistral': 6923, 'n_tokens_neox': 6687, 'n_words': 1539}