Configuring a References Resolver

We need to provide a handler for resolving references and obtain the content they refer. In our case the element which has references is ref and the attribute indicating the referred resource is location. We will have to implement a Java extension class for obtaining the referred resources.

 
  1. Create a new Java project, in your IDE.

    Create the lib directory in the Java project directory and copy in it the oxygen.jar file from the {oXygen_installation_directory}/lib directory.

  2. Create the class simple.documentation.framework.ReferencesResolver. This class must implement the ro.sync.ecss.extensions.api.AuthorReferenceResolver interface.

    import ro.sync.ecss.extensions.api.AuthorReferenceResolver;
    import ro.sync.ecss.extensions.api.AuthorAccess;
    import ro.sync.ecss.extensions.api.node.AttrValue;
    import ro.sync.ecss.extensions.api.node.AuthorElement;
    import ro.sync.ecss.extensions.api.node.AuthorNode;
    
    public class ReferencesResolver 
          implements AuthorReferenceResolver {

    The method hasReferences verifies if the handler considers the node to have references. It takes as argument an AuthorNode that represents the node which will be verified. The method will return true if the node is considered to have references. In our case, to be a reference the node must be an element with the name ref and it must have an attribute named location.

    public boolean hasReferences(AuthorNode node) {
      boolean hasReferences = false;
      if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
        AuthorElement element = (AuthorElement) node;
        if ("ref".equals(element.getLocalName())) {
          AttrValue attrValue = element.getAttribute("location");
          hasReferences = attrValue != null;
        }
      }
      return hasReferences;
    }

    The method getDisplayName returns the display name of the node that contains the expanded referred content. It takes as argument an AuthorNode that represents the node for which the display name is needed. The referred content engine will ask this AuthorReferenceResolver implementation what is the display name for each node which is considered a reference. In our case the display name is the value of the location attribute from the ref element.

    public String getDisplayName(AuthorNode node) {
      String displayName = "ref-fragment";
      if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
        AuthorElement element = (AuthorElement) node;
        if ("ref".equals(element.getLocalName())) {
          AttrValue attrValue = element.getAttribute("location");
          if (attrValue != null) {
            displayName = attrValue.getValue();
          }
        }
      }
      return displayName;
    }

    The method resolveReference resolves the reference of the node and returns a SAXSource with the parser and the parser's input source. It takes as arguments an AuthorNode that represents the node for which the reference needs resolving, the systemID of the node, the AuthorAccess with access methods to the Author data model and a SAX EntityResolver which resolves resources that are already opened in another editor or resolve resources through the XML catalog. In our implementation we need to resolve the reference relative to the systemID, and create a parser and an input source over the resolved reference.

    public SAXSource resolveReference(
        AuthorNode node, 
        String systemID, 
        AuthorAccess authorAccess,
        EntityResolver  entityResolver) {
      SAXSource saxSource = null;
      
      if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
        AuthorElement element = (AuthorElement) node;
        if ("ref".equals(element.getLocalName())) {
          AttrValue attrValue = element.getAttribute("location");
          if (attrValue != null) {
            String attrStringVal = attrValue.getValue();
            try {
              URL absoluteUrl = new URL(new URL(systemID), 
                  authorAccess.correctURL(attrStringVal));
              
              InputSource inputSource = entityResolver.resolveEntity(null, 
                  absoluteUrl.toString());
              if(inputSource == null) {
                inputSource = new InputSource(absoluteUrl.toString());
              }
              
              XMLReader xmlReader = authorAccess.newNonValidatingXMLReader();
              xmlReader.setEntityResolver(entityResolver);
              
              saxSource = new SAXSource(xmlReader, inputSource);
            } catch (MalformedURLException e) {
              logger.error(e, e);
            } catch (SAXException e) {
              logger.error(e, e);
            } catch (IOException e) {
              logger.error(e, e);
            }
          }
        }
      }
    
      return saxSource;
    }

    The method getReferenceUniqueIDshould return an unique identifier for the node reference. The unique identifier is used to avoid resolving the references recursively. It takes as argument an AuthorNode that represents the node with the reference. In our implementation the unique identifier is the value of the location attribute from the ref element.

    public String getDisplayName(AuthorNode node) {
      String displayName = "ref-fragment";
      if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
        AuthorElement element = (AuthorElement) node;
        if ("ref".equals(element.getLocalName())) {
          AttrValue attrValue = element.getAttribute("location");
          if (attrValue != null) {
            displayName = attrValue.getValue();
          }
        }
      }
      return displayName;
    }

    The method getReferenceSystemIDshould return the systemID of the referred content. It takes as arguments an AuthorNode that represents the node with the reference and the AuthorAccess with access methods to the Author data model. In our implementation the we use the value of the location attribute from the ref element and resolve it relatively to the XML base URL of the node.

    public String getReferenceSystemID(AuthorNode node, 
                                       AuthorAccess authorAccess) {
      String systemID = null;
      if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
        AuthorElement element = (AuthorElement) node;
        if ("ref".equals(element.getLocalName())) {
          AttrValue attrValue = element.getAttribute("location");
          if (attrValue != null) {
            String attrStringVal = attrValue.getValue();
            try {
              URL absoluteUrl = new URL(node.getXMLBaseURL(), 
                  authorAccess.correctURL(attrStringVal));
              systemID = absoluteUrl.toString();
            } catch (MalformedURLException e) {
              logger.error(e, e);
            }
          }
        }
      }
      return systemID;
    }

    The complete source code of our implementation is found in the Example Files Listings, the Java Files section.

  3. Package the compiled class into a jar file.

  4. Copy the jar file into the frameworks/sdf directory.

  5. Add the jar file to the Author class path.

  6. Register the Java class by clicking on the References resolver label. Press the Choose button and select from the displayed dialog the name of the class: ReferencesResolver.

In the listing below, the XML document contains the ref element:

<ref location="referred.xml">Reference</ref>

When reference resolver is specified, the reference has the following layout:

 

Figure 7.28. Reference with no specified reference resolver

Reference with no specified reference resolver

When the above implementation is configured, the reference has the expected layout:

 

Figure 7.29. Reference with reference resolver

Reference with reference resolver