/* * RoleTypeInitializer.java * * Created on May 6, 2010, 6:00:44 PM * * Description: Initializes role types from an XML file. * * Copyright (C) May 6, 2010, Stephen L. Reed. * * This program 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 3 of the License, or (at your option) any later version. * * This program 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 this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.ahcsSupport; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import net.jcip.annotations.NotThreadSafe; import net.sf.ehcache.CacheManager; import org.apache.log4j.Logger; import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.texai.ahcsSupport.domainEntity.RoleType; import org.texai.ahcsSupport.domainEntity.SkillClass; import org.texai.kb.CacheInitializer; import org.texai.kb.Constants; import org.texai.kb.persistence.DistributedRepositoryManager; import org.texai.kb.persistence.RDFEntityManager; import org.texai.util.TexaiException; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** Initializes role types from an XML file. * * @author reed */ @NotThreadSafe public class RoleTypeInitializer { /** the log4j logger */ private static final Logger LOGGER = Logger.getLogger(RoleTypeInitializer.class); /** the indicator whether debug logging is enabled */ private static final boolean IS_DEBUG_LOGGING_ENABLED = LOGGER.isDebugEnabled(); /** the RDF entity manager */ private RDFEntityManager rdfEntityManager; /** the id assigned by the persistence framework for the role type */ private URI id; /** the role type ids */ private final List<URI> ids = new ArrayList<>(); /** the role type name */ private String typeName; /** the names of inherited role types */ private Set<String> inheritedRoleTypeNames = new HashSet<>(); /** the skill class name */ private String skillClassName; /** the skill class uses */ private Set<SkillClass> skillClasses = new HashSet<>(); /** the role's description in English */ private String description; /** the Albus hierarchical control system granularity level */ private URI albusHCSGranularityLevel; /** the inherited role type dictionary, id --> set of names of inherited role types */ private final Map<URI, Set<String>> inheritedRoleTypeDictionary = new HashMap<>(); /** the node access */ private NodeAccess nodeAccess; /** the indicator that parsing is within the scope of a <skill-class> tag */ private boolean isWithinSkillClassScope = false; /** the id assigned by the persistence framework for the skill use */ private URI skillUseId; /** Constructs a new RoleTypeInitializer instance. */ public RoleTypeInitializer() { } /** Initializes the application. * * @param rdfEntityManager the RDF entity manager * rule unit test specifications */ public void initialize(final RDFEntityManager rdfEntityManager) { //Preconditions assert rdfEntityManager != null : "rdfEntityManager must not be null"; this.rdfEntityManager = rdfEntityManager; nodeAccess = new NodeAccess(rdfEntityManager); } /** Reads the file and persists the role types after all have been parsed. * * @param roleTypesPath role types file path */ public void process(final String roleTypesPath) { //Preconditions assert roleTypesPath != null : "roleTypesPath must not be null"; assert !roleTypesPath.isEmpty() : "roleTypesPath must not be an empty string"; resetState(); final BufferedInputStream bufferedInputStream; try { final File roleTypesFile = new File(roleTypesPath); LOGGER.info("parsing the role types file: " + roleTypesFile.toString()); bufferedInputStream = new BufferedInputStream(new FileInputStream(roleTypesFile)); } catch (final FileNotFoundException ex) { throw new TexaiException(ex); } try { final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); final SAXParser saxParser = saxParserFactory.newSAXParser(); final SAXHandler myHandler = new SAXHandler(); saxParser.parse(bufferedInputStream, myHandler); } catch (final ParserConfigurationException | SAXException | IOException ex) { throw new TexaiException(ex); } resolveInheritedRoleTypeNames(); displayLoadedRoleTypes(); try { final RepositoryConnection repositoryConnection = DistributedRepositoryManager.getInstance().getRepositoryConnectionForRepositoryName("NodeRoleTypes"); LOGGER.info("repository size: " + repositoryConnection.size()); repositoryConnection.close(); } catch (final RepositoryException ex) { throw new TexaiException(ex); } } /** Provides a SAX parsing handler. */ final class SAXHandler extends DefaultHandler { /** the string builder */ private final StringBuilder stringBuilder = new StringBuilder(); /** Constructs a new SAXHandler instance. */ public SAXHandler() { } /** Receives notification of the start of an element. * * @param uri the element tag * @param localName the local name * @param qName the qualified name * @param attributes the attributes */ @Override public void startElement( final String uri, final String localName, final String qName, final Attributes attributes) { //Preconditions assert qName != null : "qName must not be null"; assert !qName.isEmpty() : "qName must not be empty"; if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("startElement qName: " + qName); } stringBuilder.setLength(0); switch (qName) { case "semantics": case "statements": case "skill-class": isWithinSkillClassScope = true; break; } } /** Receive notification of character data inside an element. * * @param characters the characters * @param start the start position in the character array * @param length the length of the character string */ @Override public void characters(final char[] characters, final int start, final int length) { //LOGGER.debug("characters, start: " + start + ", length: " + length); final int end = start + length; for (int i = start; i < end; i++) { stringBuilder.append(characters[i]); } } /** Receives notification of the end of an element. * * @param uri the element tag * @param localName the local name * @param qName the qualified name */ @Override public void endElement( final String uri, final String localName, final String qName) { //Preconditions assert qName != null : "qName must not be null"; assert !qName.isEmpty() : "qName must not be empty"; assert rdfEntityManager != null : "rdfEntityManager must not be null"; if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("endElement qName: " + qName); LOGGER.debug("stringBuilder: " + stringBuilder.toString()); } if (qName.equals("id") && stringBuilder.length() > 0) { if (isWithinSkillClassScope) { try { skillUseId = new URIImpl(stringBuilder.toString()); } catch (IllegalArgumentException ex) { LOGGER.fatal("invalid id URI: '" + stringBuilder.toString() + "'"); throw new TexaiException(ex); } } else { try { id = new URIImpl(stringBuilder.toString()); } catch (IllegalArgumentException ex) { LOGGER.fatal("invalid id URI: '" + stringBuilder.toString() + "'"); throw new TexaiException(ex); } } } else if (qName.equals("name")) { typeName = stringBuilder.toString(); } else if (qName.equals("inherited-role-type-name") && stringBuilder.length() > 0) { inheritedRoleTypeNames.add(stringBuilder.toString()); } else if (qName.equals("skill-class-name") && stringBuilder.length() > 0) { skillClassName = stringBuilder.toString(); } else if (qName.equals("skill-class")) { final SkillClass skillClass = new SkillClass(skillClassName); if (skillUseId != null) { rdfEntityManager.setIdFor(skillClass, skillUseId); } skillClasses.add(skillClass); skillClassName = null; isWithinSkillClassScope = false; LOGGER.info("persisting " + skillClass); rdfEntityManager.persist(skillClass); } else if (qName.equals("description")) { description = stringBuilder.toString(); } else if (qName.equals("granularity-level")) { albusHCSGranularityLevel = new URIImpl(Constants.TEXAI_NAMESPACE + stringBuilder.toString()); } else if (qName.equals("role-type")) { RoleType roleType = nodeAccess.findRoleType(typeName); if (roleType == null) { roleType = new RoleType(); } else { roleType.clearSkillUses(); } if (albusHCSGranularityLevel != null) { roleType.setAlbusHCSGranularityLevel(albusHCSGranularityLevel); } if (description != null && !description.isEmpty()) { roleType.setDescription(description); } if (typeName != null && !typeName.isEmpty()) { roleType.setTypeName(typeName); } for (final SkillClass skillUse : skillClasses) { roleType.addSkillUse(skillUse); } if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("\n" + roleType.toXML()); } if (id != null) { rdfEntityManager.setIdFor(roleType, id); } rdfEntityManager.persist(roleType); LOGGER.info("persisting: " + roleType.toString() + " id: " + roleType.getId()); assert isLoadedCorrectly(roleType); ids.add(roleType.getId()); if (!inheritedRoleTypeNames.isEmpty()) { inheritedRoleTypeDictionary.put(roleType.getId(), Collections.unmodifiableSet(inheritedRoleTypeNames)); } resetState(); } stringBuilder.setLength(0); } } /** Resets the parsing state for the next role type definition. */ private void resetState() { id = null; typeName = null; inheritedRoleTypeNames = new HashSet<>(); skillClassName = null; skillUseId = null; skillClasses = new HashSet<>(); isWithinSkillClassScope = false; description = null; albusHCSGranularityLevel = null; } /** Verifies that the given role type has been persisted correctly. * * @param roleType the given role type * @return true if no assertion errors occur. */ private boolean isLoadedCorrectly(final RoleType roleType) { //Preconditions assert nodeAccess != null : "nodeAccess must not be null"; final List<RoleType> existingRoleTypes = nodeAccess.findRoleTypes(roleType.getTypeName()); assert existingRoleTypes.size() == 1; if (IS_DEBUG_LOGGING_ENABLED) { LOGGER.debug("persisted\n" + roleType.toXML()); LOGGER.debug("loaded\n" + existingRoleTypes.get(0).toXML()); } LOGGER.info("persisted\n" + roleType.toXML()); LOGGER.info("loaded\n" + existingRoleTypes.get(0).toXML()); assert existingRoleTypes.get(0).toXML().equals(roleType.toXML()); return true; } /** Resolves inherited role type names. */ private void resolveInheritedRoleTypeNames() { //Preconditions assert nodeAccess != null : "nodeAccess must not be null"; assert rdfEntityManager != null : "rdfEntityManager must not be null"; // resolve the inherited role type names, which might have been forward references for (final Entry<URI, Set<String>> entry : inheritedRoleTypeDictionary.entrySet()) { final URI id1 = entry.getKey(); final RoleType loadedRoleType = rdfEntityManager.find(RoleType.class, id1); assert loadedRoleType != null; final Set<String> inheritedRoleTypeNames1 = entry.getValue(); assert !inheritedRoleTypeNames1.isEmpty(); for (final String inheritedRoleTypeName : inheritedRoleTypeNames1) { final RoleType inheritedRoleType = nodeAccess.findRoleType(inheritedRoleTypeName); if (inheritedRoleType == null) { throw new TexaiException("inherited node type not found: " + inheritedRoleTypeName); } loadedRoleType.addInheritedRoleType(inheritedRoleType); } rdfEntityManager.persist(loadedRoleType); } } /** Displays the loaded role types. */ private void displayLoadedRoleTypes() { for (final URI id1 : ids) { final RoleType loadedRoleType = rdfEntityManager.find(RoleType.class, id1); LOGGER.info("\n" + loadedRoleType.toXML(2)); } } /** Finalizes this application. */ public void finalization() { LOGGER.info("RoleTypeInitializer completed"); } /** Executes this application. * * @param args the command line arguments (not used) */ public static void main(final String[] args) { CacheInitializer.initializeCaches(); DistributedRepositoryManager.clearNamedRepository("NodeRoleTypes"); final RoleTypeInitializer roleTypeInitializer = new RoleTypeInitializer(); final RDFEntityManager rdfEntityManager = new RDFEntityManager(); roleTypeInitializer.initialize(rdfEntityManager); // clear repository first roleTypeInitializer.process("data/role-types.xml"); roleTypeInitializer.finalization(); rdfEntityManager.close(); DistributedRepositoryManager.shutDown(); CacheManager.getInstance().shutdown(); } }