© 2008 syncRO soft ltd. | <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.oxygenxml.com/sample/documentation"
xmlns:doc="http://www.oxygenxml.com/sample/documentation"
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts"
elementFormDefault="qualified">
<xs:import
namespace="http://www.oxygenxml.com/sample/documentation/abstracts"
schemaLocation="abs.xsd"/>
<xs:element name="book" type="doc:sectionType"/>
<xs:element name="article" type="doc:sectionType"/>
<xs:element name="section" type="doc:sectionType"/>
<xs:complexType name="sectionType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element ref="abs:def" minOccurs="0"/>
<xs:choice>
<xs:sequence>
<xs:element ref="doc:section"
maxOccurs="unbounded"/>
</xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element ref="doc:para"/>
<xs:element ref="doc:ref"/>
<xs:element ref="doc:image"/>
<xs:element ref="doc:table"/>
</xs:choice>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:element name="para" type="doc:paragraphType"/>
<xs:complexType name="paragraphType" mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="b"/>
<xs:element name="i"/>
<xs:element name="link"/>
</xs:choice>
</xs:complexType>
<xs:element name="ref">
<xs:complexType>
<xs:attribute name="location" type="xs:anyURI"
use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="image">
<xs:complexType>
<xs:attribute name="href" type="xs:anyURI"
use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="table">
<xs:complexType>
<xs:sequence>
<xs:element name="header">
<xs:complexType>
<xs:sequence>
<xs:element name="td"
maxOccurs="unbounded"
type="doc:paragraphType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="tr" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="td"
type="doc:tdType"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="tdType">
<xs:complexContent>
<xs:extension base="doc:paragraphType">
<xs:attribute name="row_span"
type="xs:integer"/>
<xs:attribute name="column_span"
type="xs:integer"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>/* Element from another namespace */
@namespace abs "http://www.oxygenxml.com/sample/documentation/abstracts";
abs|def{
font-family:monospace;
font-size:smaller;
}
abs|def:before{
content:"Definition:";
color:gray;
}
/* Vertical flow */
book,
section,
para,
title,
image,
ref {
display:block;
}
/* Horizontal flow */
b,i {
display:inline;
}
section{
margin-left:1em;
margin-top:1em;
}
section{
foldable:true;
not-foldable-child: title;
}
link[href]:before{
display:inline;
link:attr(href);
content: "Click to open: " attr(href);
}
/* Title rendering*/
title{
font-size: 2.4em;
font-weight:bold;
}
* * title{
font-size: 2.0em;
}
* * * title{
font-size: 1.6em;
}
* * * * title{
font-size: 1.2em;
}
book,
article{
counter-reset:sect;
}
book > section,
article > section{
counter-increment:sect;
}
book > section > title:before,
article > section > title:before{
content: "Section: " counter(sect) " ";
}
/* Inlines rendering*/
b {
font-weight:bold;
}
i {
font-style:italic;
}
/*Table rendering */
table{
display:table;
border:1px solid navy;
margin:1em;
}
tr, header{
display:table-row;
}
header{
background-color: silver;
color:inherit
}
td{
display:table-cell;
border:1px solid navy;
padding:1em;
}
image{
display:block;
content: attr(href, url);
margin-left:2em;
}
<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://www.oxygenxml.com/sample/documentation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts">
<title>My Technical Book</title>
<section>
<title>XML</title>
<abs:def>Extensible Markup Language</abs:def>
<para>In this section of the book I will explain
different XML applications.</para>
</section>
<section>
<title>Accessing XML data.</title>
<section>
<title>XSLT</title>
<abs:def>Extensible stylesheet language
transformation (XSLT) is a language for
transforming XML documents into other XML
documents.</abs:def>
<para>A list of XSL elements and what they do..</para>
<table>
<header>
<td>XSLT Elements</td>
<td>Description</td>
</header>
<tr>
<td>
<b>xsl:stylesheet</b>
</td>
<td>The <i>xsl:stylesheet</i> element is
always the top-level element of an
XSL stylesheet. The name
<i>xsl:transform</i> may be used
as a synonym.</td>
</tr>
<tr>
<td>
<b>xsl:template</b>
</td>
<td>The <i>xsl:template</i> element has
an optional mode attribute. If this
is present, the template will only
be matched when the same mode is
used in the invoking
<i>xsl:apply-templates</i>
element.</td>
</tr>
<tr>
<td>
<b>for-each</b>
</td>
<td>The xsl:for-each element causes
iteration over the nodes selected by
a node-set expression.</td>
</tr>
<tr>
<td column_span="2">End of the list</td>
</tr>
</table>
</section>
<section>
<title>XPath</title>
<abs:def>XPath (XML Path Language) is a terse
(non-XML) syntax for addressing portions of
an XML document. </abs:def>
<para>Some of the XPath functions.</para>
<table>
<header>
<td>Function</td>
<td>Description</td>
</header>
<tr>
<td>format-number</td>
<td>The <i>format-number</i> function
converts its first argument to a
string using the format pattern
string specified by the second
argument and the decimal-format
named by the third argument, or the
default decimal-format, if there is
no third argument</td>
</tr>
<tr>
<td>current</td>
<td>The <i>current</i> function returns
a node-set that has the current node
as its only member.</td>
</tr>
<tr>
<td>generate-id</td>
<td>The <i>generate-id</i> function
returns a string that uniquely
identifies the node in the argument
node-set that is first in document
order.</td>
</tr>
</table>
</section>
</section>
<section>
<title>Documentation frameworks</title>
<para>One of the most important documentation
frameworks is Docbook.</para>
<image
href="http://www.xmlhack.com/images/docbook.gif"/>
<para>The other is the topic oriented DITA, promoted
by OASIS.</para>
<image
href="http://www.oasis-open.org/images/standards/oasis_standard.jpg"
/>
</section>
</book>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xpath-default-namespace=
"http://www.oxygenxml.com/sample/documentation">
<xsl:template match="/">
<html><xsl:apply-templates/></html>
</xsl:template>
<xsl:template match="section">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="image">
<img src="{@href}"/>
</xsl:template>
<xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="abs:def"
xmlns:abs=
"http://www.oxygenxml.com/sample/documentation/abstracts">
<p>
<u><xsl:apply-templates/></u>
</p>
</xsl:template>
<xsl:template match="title">
<h1><xsl:apply-templates/></h1>
</xsl:template>
<xsl:template match="b">
<b><xsl:apply-templates/></b>
</xsl:template>
<xsl:template match="i">
<i><xsl:apply-templates/></i>
</xsl:template>
<xsl:template match="table">
<table frame="box" border="1px">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="header">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="tr">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="td">
<td>
<xsl:apply-templates/>
</td>
</xsl:template>
<xsl:template match="header/header/td">
<th>
<xsl:apply-templates/>
</th>
</xsl:template>
</xsl:stylesheet>
package simple.documentation.framework;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.MalformedURLException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
import ro.sync.ecss.extensions.api.ArgumentsMap;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorOperation;
import ro.sync.ecss.extensions.api.AuthorOperationException;
public class InsertImageOperation implements AuthorOperation {
//
// Implementing the Author Operation Interface.
//
/**
* Performs the operation.
*/
public void doOperation(AuthorAccess authorAccess,
ArgumentsMap arguments)
throws IllegalArgumentException,
AuthorOperationException {
JFrame oxygenFrame = (JFrame) authorAccess.getParentFrame();
String href = displayURLDialog(oxygenFrame);
if (href.length() != 0) {
// Creates the image XML fragment.
String imageFragment =
"<image xmlns='http://www.oxygenxml.com/sample/documentation'" +
" href='" + href + "'/>";
// Inserts this fragment at the caret position.
int caretPosition = authorAccess.getCaretOffset();
authorAccess.insertXMLFragment(imageFragment, caretPosition);
}
}
/**
* Has no arguments.
*
* @return null.
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @return A description of the operation.
*/
public String getDescription() {
return "Inserts an image element. Asks the" +
" user for a URL reference.";
}
//
// End of interface implementation.
//
//
// Auxiliary methods.
//
/**
* Displays the URL dialog.
*
* @param parentFrame The parent frame for
* the dialog.
* @return The selected URL string value,
* or the empty string if the user canceled
* the URL selection.
*/
private String displayURLDialog(JFrame parentFrame) {
final JDialog dlg = new JDialog(parentFrame,
"Enter the value for the href attribute", true);
JPanel mainContent = new JPanel(new GridBagLayout());
// The text field.
GridBagConstraints cstr = new GridBagConstraints();
cstr.gridx = 0;
cstr.gridy = 0;
cstr.weightx = 0;
cstr.gridwidth = 1;
cstr.fill = GridBagConstraints.HORIZONTAL;
mainContent.add(new JLabel("Image URI:"), cstr);
cstr.gridx = 1;
cstr.weightx = 1;
final JTextField urlField = new JTextField();
urlField.setColumns(15);
mainContent.add(urlField, cstr);
// Add the "Browse button."
cstr.gridx = 2;
cstr.weightx = 0;
JButton browseButton = new JButton("Browse");
browseButton.addActionListener(new ActionListener() {
/**
* Shows a file chooser dialog.
*/
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(false);
// Accepts only the image files.
fileChooser.setFileFilter(new FileFilter() {
public String getDescription() {
return "Image files";
}
public boolean accept(File f) {
String fileName = f.getName();
return f.isFile() &&
( fileName.endsWith(".jpeg")
|| fileName.endsWith(".jpg")
|| fileName.endsWith(".gif")
|| fileName.endsWith(".png")
|| fileName.endsWith(".svg"));
}
});
if (fileChooser.showOpenDialog(dlg)
== JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
try {
// Set the file into the text field.
urlField.setText(file.toURL().toString());
} catch (MalformedURLException ex) {
// This should not happen.
ex.printStackTrace();
}
}
}
});
mainContent.add(browseButton, cstr);
// Add the "Ok" button to the layout.
cstr.gridx = 0;
cstr.gridy = 1;
cstr.weightx = 0;
JButton okButton = new JButton("Ok");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dlg.setVisible(false);
}
});
mainContent.add(okButton, cstr);
mainContent.setBorder(
BorderFactory.createEmptyBorder(10, 5, 10, 5));
// Add the "Cancel" button to the layout.
cstr.gridx = 2;
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
urlField.setText("");
dlg.setVisible(false);
}
});
mainContent.add(cancelButton, cstr);
// When the user closes the dialog
// from the window decoration,
// assume "Cancel" action.
dlg.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
urlField.setText("");
}
});
dlg.getContentPane().add(mainContent);
dlg.pack();
dlg.setLocationRelativeTo(parentFrame);
dlg.setVisible(true);
return urlField.getText();
}
/**
* Test method.
*
* @param args The arguments are ignored.
*/
public static void main(String[] args) {
InsertImageOperation operation =
new InsertImageOperation();
System.out.println("Choosen URL: " +
operation.displayURLDialog(new JFrame()));
}
}package simple.documentation.framework;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Properties;
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
import ro.sync.ecss.extensions.api.ArgumentsMap;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorOperation;
import ro.sync.ecss.extensions.api.AuthorOperationException;
public class QueryDatabaseOperation implements AuthorOperation{
private static String ARG_JDBC_DRIVER ="jdbc_driver";
private static String ARG_USER ="user";
private static String ARG_PASSWORD ="password";
private static String ARG_SQL ="sql";
private static String ARG_CONNECTION ="connection";
/**
* @return The array of arguments the developer must specify when
* configuring the action.
*/
public ArgumentDescriptor[] getArguments() {
ArgumentDescriptor args[] = new ArgumentDescriptor[] {
new ArgumentDescriptor(
ARG_JDBC_DRIVER,
ArgumentDescriptor.TYPE_STRING,
"The name of the Java class that is the JDBC driver."),
new ArgumentDescriptor(
ARG_CONNECTION,
ArgumentDescriptor.TYPE_STRING,
"The database URL connection string."),
new ArgumentDescriptor(
ARG_USER,
ArgumentDescriptor.TYPE_STRING,
"The name of the database user."),
new ArgumentDescriptor(
ARG_PASSWORD,
ArgumentDescriptor.TYPE_STRING,
"The database password."),
new ArgumentDescriptor(
ARG_SQL,
ArgumentDescriptor.TYPE_STRING,
"The SQL statement to be executed.")
};
return args;
}
/**
* @return The operation description.
*/
public String getDescription() {
return "Executes a database query and puts the result in a table.";
}
public void doOperation(AuthorAccess authorAccess, ArgumentsMap map)
throws IllegalArgumentException, AuthorOperationException {
// Collects the arguments.
String jdbcDriver =
(String)map.getArgumentValue(ARG_JDBC_DRIVER);
String connection =
(String)map.getArgumentValue(ARG_CONNECTION);
String user =
(String)map.getArgumentValue(ARG_USER);
String password =
(String)map.getArgumentValue(ARG_PASSWORD);
String sql =
(String)map.getArgumentValue(ARG_SQL);
int caretPosition = authorAccess.getCaretOffset();
try {
authorAccess.insertXMLFragment(
getFragment(jdbcDriver, connection, user, password, sql),
caretPosition);
} catch (SQLException e) {
throw new AuthorOperationException(
"The operation failed due to the following database error: " +
e.getMessage(), e);
} catch (ClassNotFoundException e) {
throw new AuthorOperationException(
"The JDBC database driver was not found. Tried to load ' " +
jdbcDriver + "'", e);
}
}
/**
* Creates a connection to the database, executes
* the SQL statement and creates an XML fragment
* containing a table element that wraps the data
* from the result set.
*
*
* @param jdbcDriver The class name of the JDBC driver.
* @param connectionURL The connection URL.
* @param user The database user.
* @param password The password.
* @param sql The SQL statement.
* @return The string containing the XML fragment.
*
* @throws SQLException thrown when there is a
* problem accessing the database or there are
* erors in the SQL expression.
* @throws ClassNotFoundException when the JDBC
* driver class could not be loaded.
*/
private String getFragment(
String jdbcDriver,
String connectionURL,
String user,
String password,
String sql) throws
SQLException,
ClassNotFoundException {
Properties pr = new Properties();
pr.put("characterEncoding", "UTF8");
pr.put("useUnicode", "TRUE");
pr.put("user", user);
pr.put("password", password);
// Loads the database driver.
Class.forName(jdbcDriver);
// Opens the connection
Connection connection =
DriverManager.getConnection(connectionURL, pr);
java.sql.Statement statement =
connection.createStatement();
ResultSet resultSet =
statement.executeQuery(sql);
StringBuffer fragmentBuffer = new StringBuffer();
fragmentBuffer.append(
"<table xmlns='http://www.oxygenxml.com/sample/documentation'>");
//
// Creates the table header.
//
fragmentBuffer.append("<header>");
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
fragmentBuffer.append("<td>");
fragmentBuffer.append(
xmlEscape(metaData.getColumnName(i)));
fragmentBuffer.append("</td>");
}
fragmentBuffer.append("</header>");
//
// Creates the table content.
//
while (resultSet.next()) {
fragmentBuffer.append("<tr>");
for (int i = 1; i <= columnCount; i++) {
fragmentBuffer.append("<td>");
fragmentBuffer.append(
xmlEscape(resultSet.getObject(i)));
fragmentBuffer.append("</td>");
}
fragmentBuffer.append("</tr>");
}
fragmentBuffer.append("</table>");
// Cleanup
resultSet.close();
statement.close();
connection.close();
return fragmentBuffer.toString();
}
/**
* Some of the values from the database table
* may contain characters that must be escaped
* in XML, to ensure the fragment is well formed.
*
* @param object The object from the database.
* @return The escaped string representation.
*/
private String xmlEscape(Object object) {
String str = String.valueOf(object);
return str.
replaceAll("&", "&").
replaceAll("<", "<");
}
}
package simple.documentation.framework;
public class TableCellSpanProvider
implements AuthorTableCellSpanProvider {
/**
* Extracts the integer specifing what is the width
* (in columns) of the cell
* representing in the table layout the cell element.
*/
public Integer getColSpan(AuthorElement cell) {
Integer colSpan = null;
AttrValue attrValue = cell.getAttribute("column_span");
if(attrValue != null) {
// The attribute was found.
String cs = attrValue.getValue();
if(cs != null) {
try {
colSpan = new Integer(cs);
} catch (NumberFormatException ex) {
// The attribute value was not a number.
}
}
}
return colSpan;
}
/**
* Extracts the integer specifing what is the
* height (in rows) of the cell
* representing in the table layout the cell element.
*/
public Integer getRowSpan(AuthorElement cell) {
Integer rowSpan = null;
AttrValue attrValue = cell.getAttribute("row_span");
if(attrValue != null) {
// The attribute was found.
String rs = attrValue.getValue();
if(rs != null) {
try {
rowSpan = new Integer(rs);
} catch (NumberFormatException ex) {
// The attribute value was not a number.
}
}
}
return rowSpan;
}
/**
* Ignored. We do not extract data from the
* <code>table</code> element.
*/
public void init(AuthorElement table) {
}
public String getDescription() {
return
"Implementation for the Simple Documentation Framework table layout.";
}
}
package simple.documentation.framework;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.transform.sax.SAXSource;
import org.apache.log4j.Logger;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorReferenceResolver;
import ro.sync.ecss.extensions.api.node.AttrValue;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
/**
* Resolver for content referred by elements named 'ref' with a
* 'location' attribute.
*/
public class ReferencesResolver implements AuthorReferenceResolver {
/**
* Logger for logging.
*/
private static Logger logger = Logger.getLogger(
ReferencesResolver.class.getName());
/**
* Verifies if the handler considers the node to have references.
*
* @param node The node to be analyzed.
* @return <code>true</code> if it is has references.
*/
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;
}
/**
* Returns the name of the node that contains the expanded referred content.
*
* @param node The node that contains references.
* @return The display name of the node.
*/
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;
}
/**
* Resolve the references of the node.
*
* The returning SAXSource will be used for creating the referred content
* using the parser and source inside it.
*
* @param node The clone of the node.
* @param systemID The system ID of the node with references.
* @param authorAccess The author access implementation.
* @param entityResolver The entity resolver that can be used to resolve:
*
* <ul>
* <li>Resources that are already opened in editor.
* For this case the InputSource will contains the editor content.</li>
* <li>Resources resolved through XML catalog.</li>
* </ul>
*
* @return The SAX source including the parser and the parser's input source.
*/
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;
}
/**
* Get an unique identifier for the node reference.
*
* The unique identifier is used to avoid resolving the references
* recursively.
*
* @param node The node that has reference.
* @return An unique identifier for the reference node.
*/
public String getReferenceUniqueID(AuthorNode node) {
String id = 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) {
id = attrValue.getValue();
}
}
}
return id;
}
/**
* Return the systemID of the referred content.
*
* @param node The reference node.
* @param authorAccess The author access.
*
* @return The systemID of the referred content.
*/
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;
}
/**
* Verifies if the references of the given node must be refreshed
* when the attribute with the specified name has changed.
*
* @param node The node with the references.
* @param attributeName The name of the changed attribute.
* @return <code>true</code> if the references must be refreshed.
*/
public boolean isReferenceChanged(AuthorNode node, String attributeName) {
return "location".equals(attributeName);
}
/**
* @return The description of the author extension.
*/
public String getDescription() {
return "Resolves the 'ref' references";
}
}package simple.documentation.framework;
import org.xml.sax.Attributes;
import ro.sync.ecss.extensions.api.DocumentTypeCustomRuleMatcher;
public class CustomRule implements
DocumentTypeCustomRuleMatcher {
/**
* Checks if the root namespace is the one
* of our documentation framework.
*/
public boolean matches(
String systemID,
String rootNamespace,
String rootLocalName,
String doctypePublicID,
Attributes rootAttributes) {
return
"http://www.oxygenxml.com/sample/documentation".equals(rootNamespace);
}
public String getDescription() {
return
"Checks if the current Document Type Association is matching the document.";
}
}
package ro.sync.ecss.extensions.commons;
import org.apache.log4j.Logger;
import ro.sync.ecss.extensions.api.link.ElementLocator;
import ro.sync.ecss.extensions.api.link.ElementLocatorException;
import ro.sync.ecss.extensions.api.link.ElementLocatorProvider;
import ro.sync.ecss.extensions.api.link.IDTypeVerifier;
/**
* Default implementation for locating elements based on a given link.
* Depending on the link structure the following cases are covered:
* - xinclude element scheme : element(/1/2) see
* http://www.w3.org/TR/2003/REC-xptr-element-20030325/
* - ID based links : the link represents the value of an attribute of type ID
*/
public class DefaultElementLocatorProvider implements ElementLocatorProvider {
/** * Logger for logging. */
private static Logger logger = Logger.getLogger(
DefaultElementLocatorProvider.class.getName());
/**
* @see ro.sync.ecss.extensions.api.link.ElementLocatorProvider#
* getElementLocator(ro.sync.ecss.extensions.api.link.IDTypeVerifier,
* java.lang.String)
*/
public ElementLocator getElementLocator(IDTypeVerifier idVerifier,
String link) {
ElementLocator elementLocator = null;
try {
if(link.startsWith("element(")){
// xpointer element() scheme
elementLocator = new XPointerElementLocator(idVerifier, link);
} else {
// Locate link element by ID
elementLocator = new IDElementLocator(idVerifier, link);
}
} catch (ElementLocatorException e) {
logger.warn("Exception when create element locator for link: "
+ link + ". Cause: " + e, e);
}
return elementLocator;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return
"Default implementation for locating elements based on a given link. \n" +
"The following cases are covered: xinclude element scheme "
+ "and ID based links.";
}
}
package ro.sync.ecss.extensions.commons;
import java.util.Stack;
import java.util.StringTokenizer;
import org.apache.log4j.Logger;
import ro.sync.ecss.extensions.api.link.Attr;
import ro.sync.ecss.extensions.api.link.ElementLocator;
import ro.sync.ecss.extensions.api.link.ElementLocatorException;
import ro.sync.ecss.extensions.api.link.IDTypeVerifier;
/**
* Element locator for links that have the one of the following pattern:
* <ul>
* <li>element(elementID) - locate the element with the same id</li>
* <li>element(/1/2/5) - A child sequence appearing alone identifies an
* element by means of stepwise navigation, which is directed by a
* sequence of integers separated by slashes (/); each integer n locates
* the nth child element of the previously located element. </li>
* <li>element(elementID/3/4) - A child sequence appearing after an
* NCName identifies an element by means of stepwise navigation,
* starting from the element located by the given name.</li>
* </ul>
*
*/
public class XPointerElementLocator extends ElementLocator {
/**
* Logger for logging.
*/
private static Logger logger = Logger.getLogger(
XPointerElementLocator.class.getName());
/**
* Verifies if a given attribute is of a type ID.
*/
private IDTypeVerifier idVerifier;
/**
* XPointer path, the path to locate the linked element.
*/
private String[] xpointerPath;
/**
* The stack with indexes in parent of the current iterated elements.
*/
private Stack currentElementIndexStack = new Stack();
/**
* The number of elements in xpointer path.
*/
private int xpointerPathDepth;
/**
* If true then the XPointer path starts with an element ID.
*/
private boolean startWithElementID = false;
/**
* The depth of the current element in document, incremented in startElement.
*/
private int startElementDepth = 0;
/**
* Depth in document in the last endElement event.
*/
private int endElementDepth = 0;
/**
* The index in parent of the previous iterated element. Set in endElement().
*/
private int lastIndexInParent;
/**
* Constructor.
*
* @param idVerifier Verifies if an given attribute is of type ID.
* @param link The link that gives the element position.
* @throws ElementLocatorException When the link format is not supported.
**/
public XPointerElementLocator(IDTypeVerifier idVerifier, String link)
throws ElementLocatorException {
super(link);
this.idVerifier = idVerifier;
link = link.substring("element(".length(), link.length() - 1);
StringTokenizer stringTokenizer = new StringTokenizer(link, "/", false);
xpointerPath = new String[stringTokenizer.countTokens()];
int i = 0;
while (stringTokenizer.hasMoreTokens()) {
xpointerPath[i] = stringTokenizer.nextToken();
boolean invalidFormat = false;
// Empty xpointer component is not supported
if(xpointerPath[i].length() == 0){
invalidFormat = true;
}
if(i > 0){
try {
Integer.parseInt(xpointerPath[i]);
} catch (NumberFormatException e) {
invalidFormat = true;
}
}
if(invalidFormat){
throw new ElementLocatorException(
"Only the element() scheme is supported when locating XPointer links. "
+ "Supported formats: element(elementID), element(/1/2/3),
element(elemID/2/3/4).");
}
i++;
}
if(Character.isDigit(xpointerPath[0].charAt(0))){
// This is the case when xpointer have the following pattern /1/5/7
xpointerPathDepth = xpointerPath.length;
} else {
// This is the case when xpointer starts with an element ID
xpointerPathDepth = -1;
startWithElementID = true;
}
}
/**
* @see ro.sync.ecss.extensions.api.link.ElementLocator#endElement(
* java.lang.String, java.lang.String, java.lang.String)
*/
public void endElement(String uri, String localName, String name) {
endElementDepth = startElementDepth;
startElementDepth --;
lastIndexInParent = ((Integer)currentElementIndexStack.pop()).intValue();
}
/**
* @see ro.sync.ecss.extensions.api.link.ElementLocator#startElement(
* java.lang.String, java.lang.String, java.lang.String,
* ro.sync.ecss.extensions.api.link.Attr[])
*/
public boolean startElement(String uri, String localName,
String name, Attr[] atts) {
boolean linkLocated = false;
// Increase current element document depth
startElementDepth ++;
if (endElementDepth != startElementDepth) {
// The current element is the first child of the parent
currentElementIndexStack.push(new Integer(1));
} else {
// Another element in the parent element
currentElementIndexStack.push(new Integer(lastIndexInParent + 1));
}
if (startWithElementID) {
// This the case when xpointer path starts with an element ID.
String xpointerElement = xpointerPath[0];
for (int i = 0; i < atts.length; i++) {
if(xpointerElement.equals(atts[i].getValue())){
if(idVerifier.hasIDType(
localName, uri, atts[i].getQName(), atts[i].getNamespace())){
xpointerPathDepth = startElementDepth + xpointerPath.length - 1;
break;
}
}
}
}
if(xpointerPathDepth == startElementDepth){
// check if xpointer path matches with the current element path
linkLocated = true;
try {
int xpointerIdx = xpointerPath.length - 1;
int stackIdx = currentElementIndexStack.size() - 1;
int stopIdx = startWithElementID ? 1 : 0;
while (xpointerIdx >= stopIdx && stackIdx >= 0) {
int xpointerIndex = Integer.parseInt(xpointerPath[xpointerIdx]);
int currentElementIndex = ((Integer)currentElementIndexStack.
get(stackIdx)).intValue();
if(xpointerIndex != currentElementIndex) {
linkLocated = false;
break;
}
xpointerIdx--;
stackIdx--;
}
} catch (NumberFormatException e) {
logger.warn(e,e);
}
}
return linkLocated;
}
}package ro.sync.ecss.extensions.commons;
import ro.sync.ecss.extensions.api.link.Attr;
import ro.sync.ecss.extensions.api.link.ElementLocator;
import ro.sync.ecss.extensions.api.link.ExtensionUtil;
import ro.sync.ecss.extensions.api.link.IDTypeVerifier;
/**
* Implementation of an ElementLocator that treats the link as the value of an
* attribute with the type ID.
*/
public class IDElementLocator extends ElementLocator {
/**
* Class able to tell if a given attribute is of type ID.
*/
private IDTypeVerifier idVerifier;
/**
* Constructor.
*
* @param idVerifier It tells us if an attribute is of type ID.
* @param link The link used to identify an element.
*/
public IDElementLocator(IDTypeVerifier idVerifier, String link) {
super(link);
this.idVerifier = idVerifier;
}
/**
* @see ro.sync.ecss.extensions.api.link.ElementLocator#endElement(
* java.lang.String, java.lang.String, java.lang.String)
*/
public void endElement(String uri, String localName, String name) {
// Nothing to do.
}
/**
* @see ro.sync.ecss.extensions.api.link.ElementLocator#startElement(
* java.lang.String, java.lang.String, java.lang.String,
* ro.sync.ecss.extensions.api.link.Attr[])
*/
public boolean startElement(String uri, String localName,
String name, Attr[] atts) {
boolean elementFound = false;
for (int i = 0; i < atts.length; i++) {
if (link.equals(atts[i].getValue())) {
if("xml:id".equals(atts[i].getQName())) {
// xml:id attribute
elementFound = true;
} else {
// check if attribute has ID type
String attrLocalName =
ExtensionUtil.getLocalName(atts[i].getQName());
String attrUri = atts[i].getNamespace();
if (idVerifier.hasIDType(localName, uri, attrLocalName, attrUri)) {
elementFound = true;
}
}
}
}
return elementFound;
}
}
© 2008 syncRO soft ltd. |