Copying directories

This commit is contained in:
Eric van der Vlist 2021-04-25 10:49:16 +02:00
parent c478df3a34
commit 122c73ef1f
70 changed files with 7338 additions and 0 deletions

View File

@ -0,0 +1,101 @@
# Opening a JavaScript debugger within Oxygen [JS Operations](https://www.oxygenxml.com/doc/versions/23.1/ug-editor/topics/dg-default-author-operations.html#dg-default-author-operations__jsoperation)
Even though its authors state that this operation "[can be used to execute small pieces of Javascript code](https://github.com/oxygenxml/javascript-sample-operations)",
all the Oxygen and Java public APIs are exposed and this operation can be used for fairly complex tasks.
The main limitation is then the lack of a debugger and relying on logs rapidly becomes very tedious to develop complex code.
The good news is that Oxygen uses Mozilla's [Rhino JavaScript engine](https://en.wikipedia.org/wiki/Rhino_(JavaScript_engine)) and that this engine comes with a
[debugger](https://www-archive.mozilla.org/rhino/debugger.html).
This rambling proposes a way to launch the debugger from a JS Operation.
## Installation
To do so, add this function to your commons.js file:
```javascript
startDebugger = function () {
var runnable = {
run: function () {
main.dispose();
}
}
var context = Packages.org.mozilla.javascript.Context.getCurrentContext(); // Within the current context...
var contextFactory = context.getFactory();
var scope = Packages.org.mozilla.javascript.tools.shell.Environment(runnable.__parent__); // and the scope of the runnable variable's parent ...
var main = Packages.org.mozilla.javascript.tools. debugger.Main.mainEmbedded(contextFactory, scope, 'Debugger'); // start a debugging session ...
main.setExitAction(java.lang.Runnable(runnable)); // , clean the resources at exit time...
main.setVisible(true); // and make it visible/
}
```
And use it in the script parameter of the JS operation:
```javascript
doOperation = startDebugger;
```
To call the action which uses this operation, you can attach it to a menu or tool icon or just define a shortkey and you're done !
Call this action and you'll pop up a nice vintage debugger :
![debugger.jpg](debugger.jpg)
Use the File menu to open or run the file to debug (such as [test.js]):
![debugger2.jpg](debugger2.jpg)
There are a few things to know before using it, though...
## Limitations
### Scope
I haven't found a way to simply grab the current scope object in JavaScript and the closest method I have found is:
```javascript
Packages.org.mozilla.javascript.tools.shell.Environment(runnable.__parent__)
```
This creates a scope with everything in the runnable's parent's scope augmented with the environment variables with may not be present in the current scope.
All the other methods I have tried gave scopes which were, on the contrary, reduced compared to the current scope and lacking, for instance, the authorAcces object
without which you can't interact with Oxygen.
### Functions seen as objects
Just try it by yourself in the "evaluate" window:
```javascript
function a() {}
b = function() {}
```
"a" is seen as an object and not as a function, meaning that you can't call it. "b" is, correctly, seen as a function.
### Strict behavior on instanciation.
The word "new" is mandatory when instanciating a "class" (which is not the case when a JS operation script is run out of the debugger):
```javascript
aclass = function () {
this.setFoo(1);
return this;
}
aclass.prototype.setFoo = function (v) {
this.foo = v;
}
anObject = new aclass() ; // works as expected
anobject = aclass(); // fails with a message saying that there is no setFoo function in the object
```
# Framework
This directory includes the Oxygen sample framework on which ou can test the debugger but it should work on any framework.

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system
systemId="http://www.oxygenxml.com/SDF/sdf.xsd"
uri="schema/sdf.xsd"/>
<system
systemId="http://www.oxygenxml.com/SDF/abs.xsd"
uri="schema/abs.xsd"/>
</catalog>

View File

@ -0,0 +1,17 @@
startDebugger = function () {
var runnable = {
run: function () {
main.dispose();
}
}
var context = Packages.org.mozilla.javascript.Context.getCurrentContext(); // Within the current context...
var contextFactory = context.getFactory();
var scope = Packages.org.mozilla.javascript.tools.shell.Environment(runnable.__parent__); // and the scope of the runnable variable's parent ...
var main = Packages.org.mozilla.javascript.tools. debugger.Main.mainEmbedded(contextFactory, scope, 'Debugger'); // start a debugging session ...
main.setExitAction(java.lang.Runnable(runnable)); // , clean the resources at exit time...
main.setVisible(true); // and make it visible/
}

View File

@ -0,0 +1,145 @@
/* 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{
-oxy-foldable:true;
-oxy-not-foldable-child: title;
}
link[href]:before{
display:inline;
link:attr(href);
content: "Click to open: " attr(href);
}
link:before {
content:oxy_link-text();
}
/* 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;
max-width:1000px;
min-width:150px;
}
table[width]{
width:attr(width, length);
}
table[bgcolor],
tr[bgcolor],
td[bgcolor]{
background-color:attr(bgcolor);
color:inherit;
}
tr, header{
display:table-row;
}
customcol {
text-align:left;
display:block;
color:#444444;
background-color:inherit;
margin:0.2em;
padding:2px;
font-family:monospace;
font-size:small;
}
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;
}
author, name, country {
display:block;
margin-left:20px;
}
name:before {
content: oxy_label(text, "Author: ", width, 100px, color, gray);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="21.1">
<meta>
<filters directoryPatterns="" filePatterns="\Qjs-debugger.xpr\E" positiveFilePatterns="" showHiddenFiles="false"/>
<options/>
</meta>
<projectTree name="js-debugger.xpr">
<folder path="."/>
</projectTree>
</project>

View File

@ -0,0 +1 @@
France, Spain, Great Britain

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace=
"http://www.oxygenxml.com/sample/documentation/abstracts">
<xs:element name="def" type="xs:string"/>
</xs:schema>

View File

@ -0,0 +1,112 @@
<?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:articleType"/>
<xs:element name="article" type="doc:articleType"/>
<xs:element name="section" type="doc:sectionType"/>
<xs:complexType name="sectionType">
<xs:complexContent>
<xs:restriction base="doc:articleType">
<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:restriction>
</xs:complexContent>
</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 form="qualified" minOccurs="0" name="title" type="xs:string"/>
<xs:element name="customcol" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="width" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="header" minOccurs="0">
<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:attribute name="width" type="xs:string"/>
<xs:attribute name="bgcolor" type="xs:string"/>
</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:complexType name="articleType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" name="author">
<xs:complexType>
<xs:sequence>
<xs:element name="name"/>
<xs:element name="country"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<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:schema>

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,91 @@
<?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>
<author>
<name>Jean</name>
<country>France</country>
</author>
<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>

View File

@ -0,0 +1,16 @@
package simple.documentation.framework;
import ro.sync.ecss.extensions.api.AuthorAccess;
/**
* Provides access to Author functions, to specific components corresponding to
* editor, document, workspace, tables, change tracking and utility informations and actions.
*/
public interface AuthorAccessProvider {
/**
* Gets access to Author functions and components.
*
* @return The Author access.
*/
AuthorAccess getAuthorAccess();
}

View File

@ -0,0 +1,44 @@
package simple.documentation.framework;
import org.xml.sax.Attributes;
import ro.sync.ecss.extensions.api.DocumentTypeCustomRuleMatcher;
/**
* Matching rule to the SDF document type.
*
*/
public class CustomRule implements DocumentTypeCustomRuleMatcher {
/**
* Check if the SDF document type should be used for the given document properties.
*/
public boolean matches(
String systemID,
String rootNamespace,
String rootLocalName,
String doctypePublicID,
Attributes rootAttributes) {
boolean matches = true;
int attributesCount = rootAttributes.getLength();
for (int i = 0; i < attributesCount; i++) {
String localName = rootAttributes.getLocalName(i);
if ("version".equals(localName)) {
if ("2.0".equals(rootAttributes.getValue(i))) {
// Do not match the documents with "2.0" version
matches = false;
}
}
}
return matches;
}
/**
* Description.
*/
public String getDescription() {
return "Checks if the current Document Type Association"
+ " is matching the document.";
}
}

View File

@ -0,0 +1,552 @@
package simple.documentation.framework;
import java.util.ArrayList;
import java.util.List;
import ro.sync.contentcompletion.xml.CIAttribute;
import ro.sync.contentcompletion.xml.CIElement;
import ro.sync.contentcompletion.xml.CIElementAdapter;
/**
* Simple Documentation Framework element.
*/
public class SDFElement extends CIElementAdapter {
/**
* The namespace.
*/
protected String namespace = null;
/**
* The element name.
*/
protected String name = null;
/**
* The proxy.
*/
protected String proxy = null;
/**
* The type description.
*/
private String typeDescription = null;
/**
* List of attributes.
*/
protected List<CIAttribute> attributes = null;
/**
* The possible values as <code>String</code> list.
*/
private List<String> possiblesValuesList;
/**
* The element model description.
*/
private String modelDescription;
/**
* The facets. One facet can be null if it is not defined.
*/
private String lengthFacetValue;
/**
* The content type of the element.
*/
private int contentType = CONTENT_TYPE_NOT_DETERMINED;
/**
* True if content is nillable. Used only for XML Schema.
*/
private boolean nillable;
/**
* Facet values.
*/
private String minLengthFacetValue;
private String maxLengthFacetValue;
private String whitespaceFacetValue;
private String minInclusiveFacetValue;
private String minExclusiveFacetValue;
private String maxInclusiveFacetValue;
private String maxExclusiveFacetValue;
private String totalDigitsFacetValue;
private String fractionDigitsFacetValue;
private String facetPatternValue;
/**
* Guess some following elements if possible
*/
protected List<CIElement> guessElements = null;
/**
* @see ro.sync.contentcompletion.xml.CIElement#getGuessElements()
*/
@Override
public List<CIElement> getGuessElements() {
if (guessElements != null && guessElements.isEmpty()) {
// Return null is the list is empty.
return null;
}
return guessElements;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#addGuessElement(ro.sync.contentcompletion.xml.CIElement)
*/
@Override
public void addGuessElement(CIElement e) {
if (guessElements == null) {
guessElements = new ArrayList<CIElement>();
}
guessElements.add(e);
}
/**
* @return The string representing the name or null.
*/
@Override
public String getName(){
return name;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#getNamespace()
*/
@Override
public String getNamespace(){
return namespace;
}
/**
* True if the element has a namespace.
*/
private boolean xmlns;
/**
* @see ro.sync.contentcompletion.xml.CIElement#setDeclareXmlns(boolean)
*/
@Override
public void setDeclareXmlns(boolean xmlns) {
this.xmlns = xmlns;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setContentType(int)
*/
@Override
public void setContentType(int contentType) {
this.contentType = contentType;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#isEmpty()
*/
@Override
public boolean isEmpty() {
return getContentType() == CONTENT_TYPE_EMPTY;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#getContentType()
*/
@Override
public int getContentType() {
return contentType;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#isDeclareXmlns()
*/
@Override
public boolean isDeclareXmlns() {
return xmlns;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setName(java.lang.String)
*/
@Override
public void setName(String name) {
this.name = name;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setPrefix(java.lang.String)
*/
@Override
public void setPrefix(String proxy) {
this.proxy = proxy;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setNamespace(java.lang.String)
*/
@Override
public void setNamespace(String namespace) {
this.namespace = namespace;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#getQName()
*/
@Override
public String getQName() {
if (getPrefix() != null && !"".equals(getPrefix())) {
return getPrefix() + ":" + getName();
} else {
return getName();
}
}
/***
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
boolean result = false;
if (o instanceof SDFElement) {
CIElement cie = (CIElement) o;
result = compareTo(cie) == 0;
}
return result;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#getAttributes()
*/
@Override
public List<CIAttribute> getAttributes() {
return attributes;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setAttributes(java.util.List)
*/
@Override
public void setAttributes(List<CIAttribute> attributes) {
this.attributes = attributes;
}
/**
* Concatenates the name and the namespace and compares it with the other name and namespace,
* as strings.
* @param other The object to compare to.
* @return The value <code>0</code> if the argument object is equal to this object.
*/
@Override
public int compareTo(CIElement other){
String n1 = getName() == null ? "": getName();
String nm1 = getNamespace() == null ? "": getNamespace();
String n2 = other.getName() == null ? "": other.getName();
String nm2 = other.getNamespace() == null ? "": other.getNamespace();
int result = n1.compareTo(n2);
if(result == 0) {
result = nm1.compareTo(nm2);
}
return result;
}
/**
* Return the name.
*
* @return The name.
*/
@Override
public String toString(){
String toRet = String.valueOf(getName());
return toRet;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#hasPrefix()
*/
@Override
public boolean hasPrefix() {
return getPrefix() != null && !"".equals(getPrefix());
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#getPrefix()
*/
@Override
public String getPrefix() {
return proxy;
}
/**
* @param modelDescription The modelDescription to set.
*/
@Override
public void setModelDescription(String modelDescription) {
this.modelDescription = modelDescription;
}
/**
* @param fractionDigitsFacetValue The fractionDigitsFacetValue to set.
*/
@Override
public void setFacetFractionDigitsValue(String fractionDigitsFacetValue) {
this.fractionDigitsFacetValue = fractionDigitsFacetValue;
}
/**
* @param maxExclusiveFacetValue The maxExclusiveFacetValue to set.
*/
@Override
public void setFacetMaxExclusiveValue(String maxExclusiveFacetValue) {
this.maxExclusiveFacetValue = maxExclusiveFacetValue;
}
/**
* @param maxInclusiveFacetValue The maxInclusiveFacetValue to set.
*/
@Override
public void setFacetMaxInclusiveValue(String maxInclusiveFacetValue) {
this.maxInclusiveFacetValue = maxInclusiveFacetValue;
}
/**
* @param maxLengthFacetValue The maxLengthFacetValue to set.
*/
@Override
public void setFacetMaxLengthValue(String maxLengthFacetValue) {
this.maxLengthFacetValue = maxLengthFacetValue;
}
/**
* @param minInclusiveFacetValue The minInclusiveFacetValue to set.
*/
@Override
public void setFacetMinInclusiveValue(String minInclusiveFacetValue) {
this.minInclusiveFacetValue = minInclusiveFacetValue;
}
/**
* @param possiblesValuesList The possiblesValuesList to set.
*/
@Override
public void setPossiblesValues(List<String> possiblesValuesList) {
this.possiblesValuesList = possiblesValuesList;
}
/**
* @param totalDigitsFacetValue The totalDigitsFacetValue to set.
*/
@Override
public void setFacetTotalDigitsValue(String totalDigitsFacetValue) {
this.totalDigitsFacetValue = totalDigitsFacetValue;
}
/**
* @param whitespaceFacetValue The whitespaceFacetValue to set.
*/
@Override
public void setFacetWhitespaceValue(String whitespaceFacetValue) {
this.whitespaceFacetValue = whitespaceFacetValue;
}
/**
* @param lengthFacetValue The lengthFacetValue to set.
*/
@Override
public void setFacetLengthValue(String lengthFacetValue) {
this.lengthFacetValue = lengthFacetValue;
}
/**
* @param minLengthFacetValue The minLengthFacetValue to set.
*/
@Override
public void setFacetMinLengthValue(String minLengthFacetValue) {
this.minLengthFacetValue = minLengthFacetValue;
}
/**
* @param minExclusiveFacetValue The minExclusiveFacetValue to set.
*/
@Override
public void setFacetMinExclusiveValue(String minExclusiveFacetValue) {
this.minExclusiveFacetValue = minExclusiveFacetValue;
}
/**
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetFractionDigitsValue()
*/
@Override
public String getFacetFractionDigitsValue() {
return fractionDigitsFacetValue;
}
/**
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetTotalDigitsValue()
*/
@Override
public String getFacetTotalDigitsValue() {
return totalDigitsFacetValue;
}
/**
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetMaxInclusiveValue()
*/
@Override
public String getFacetMaxInclusiveValue() {
return maxInclusiveFacetValue;
}
/**
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetMaxExclusiveValue()
*/
@Override
public String getFacetMaxExclusiveValue() {
return maxExclusiveFacetValue;
}
/**
* Get the value of MIN_INCLUSIVE facet, can be null if it is not defined.
*
* @return The value of MIN_INCLUSIVE facet.
*/
@Override
public String getFacetMinInclusiveValue() {
return minInclusiveFacetValue;
}
/**
* Get the value of MIN_EXCLUSIVE facet, can be null if it is not defined.
*
* @return The value of MIN_EXCLUSIVE facet.
*/
@Override
public String getFacetMinExclusiveValue() {
return minExclusiveFacetValue;
}
/**
* Get the possible values as <code>String</code> list.
*
* @return The possible values.
*/
@Override
public List<String> getPossibleValues() {
return possiblesValuesList;
}
/**
* Get the model description.
* @return The model description.
*/
@Override
public String getModelDescription() {
return modelDescription;
}
/**
* Get the value of LENGTH facet, can be null if it is not defined.
*
* @return The value of length facet.
*/
@Override
public String getFacetLengthValue() {
return lengthFacetValue;
}
/**
* Get the value of MIN LENGTH facet, can be null if it is not defined.
*
* @return The value of MIN LENGTH facet.
*/
@Override
public String getFacetMinLengthValue() {
return minLengthFacetValue;
}
/**
* Get the value of MAX LENGTH facet, can be null if it is not defined.
*
* @return The value of MAX LENGTH facet.
*/
@Override
public String getFacetMaxLengthValue() {
return maxLengthFacetValue;
}
/**
* Get the value of WHITESPACE facet, can be null if it is not defined.
*
* @return The value of WHITESPACE facet.
*/
@Override
public String getFacetWhitespaceValue() {
return whitespaceFacetValue;
}
/**
* Get the list with pattern facets.
*
* @return The list with pattern facets, can be null if no FACET_PATTERN defined.
*/
@Override
public String getFacetPattern(){
return facetPatternValue;
}
/**
* Set the list with pattern facets.
*
* @param patternFacets The list with pattern facets.
*/
@Override
public void setFacetPattern(String patternFacets){
this.facetPatternValue = patternFacets;
}
/**
* The annotation string, null as default.
*/
protected String annotation;
/**
* Get the annotation for the element.
*
* @return A text that explain how to use the attribute, or null.
*/
@Override
public String getAnnotation() {
return annotation;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setAnnotation(java.lang.String)
*/
@Override
public void setAnnotation(String annotation){
this.annotation = annotation;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#getTypeDescription()
*/
@Override
public String getTypeDescription() {
return typeDescription;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setTypeDescription(java.lang.String)
*/
@Override
public void setTypeDescription(String typeDescription) {
this.typeDescription = typeDescription;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#setNillable(boolean)
*/
@Override
public void setNillable(boolean nillable) {
this.nillable = nillable;
}
/**
* @see ro.sync.contentcompletion.xml.CIElement#isNillable()
*/
@Override
public boolean isNillable() {
return nillable;
}
}

View File

@ -0,0 +1,173 @@
package simple.documentation.framework;
import ro.sync.contentcompletion.xml.SchemaManagerFilter;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorExtensionStateListener;
import ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler;
import ro.sync.ecss.extensions.api.AuthorReferenceResolver;
import ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler;
import ro.sync.ecss.extensions.api.AuthorTableCellSpanProvider;
import ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider;
import ro.sync.ecss.extensions.api.CustomAttributeValueEditor;
import ro.sync.ecss.extensions.api.ExtensionsBundle;
import ro.sync.ecss.extensions.api.StylesFilter;
import ro.sync.ecss.extensions.api.link.ElementLocatorProvider;
import ro.sync.ecss.extensions.api.link.LinkTextResolver;
import ro.sync.ecss.extensions.api.structure.AuthorBreadCrumbCustomizer;
import ro.sync.ecss.extensions.api.structure.AuthorOutlineCustomizer;
import ro.sync.ecss.extensions.commons.DefaultElementLocatorProvider;
import simple.documentation.framework.extensions.SDFAttributesValueEditor;
import simple.documentation.framework.extensions.SDFAuthorBreadCrumbCustomizer;
import simple.documentation.framework.extensions.SDFAuthorExtensionStateListener;
import simple.documentation.framework.extensions.SDFAuthorOutlineCustomizer;
import simple.documentation.framework.extensions.SDFExternalObjectInsertionHandler;
import simple.documentation.framework.extensions.SDFReferencesResolver;
import simple.documentation.framework.extensions.SDFSchemaAwareEditingHandler;
import simple.documentation.framework.extensions.SDFSchemaManagerFilter;
import simple.documentation.framework.extensions.SDFStylesFilter;
import simple.documentation.framework.extensions.TableCellSpanProvider;
import simple.documentation.framework.extensions.TableColumnWidthProvider;
/**
* Simple Document Framework extension bundle.
*
*/
public class SDFExtensionsBundle extends ExtensionsBundle {
/**
* Simple documentation framework state listener.
*/
private SDFAuthorExtensionStateListener sdfAuthorExtensionStateListener;
/**
* Editor for attributes values.
*/
@Override
public CustomAttributeValueEditor createCustomAttributeValueEditor(boolean forEclipsePlugin) {
return new SDFAttributesValueEditor(new AuthorAccessProvider() {
public AuthorAccess getAuthorAccess() {
AuthorAccess access = null;
if (sdfAuthorExtensionStateListener != null) {
// Get the Author access.
access = sdfAuthorExtensionStateListener.getAuthorAccess();
}
return access;
}
});
}
/**
* Simple documentation framework state listener.
*/
@Override
public AuthorExtensionStateListener createAuthorExtensionStateListener() {
sdfAuthorExtensionStateListener = new SDFAuthorExtensionStateListener();
return sdfAuthorExtensionStateListener;
}
/**
* Filter for content completion proposals from the schema manager.
*/
@Override
public SchemaManagerFilter createSchemaManagerFilter() {
return new SDFSchemaManagerFilter();
}
/**
* Default element locator.
*/
@Override
public ElementLocatorProvider createElementLocatorProvider() {
return new DefaultElementLocatorProvider();
}
/**
* Expand content references.
*/
@Override
public AuthorReferenceResolver createAuthorReferenceResolver() {
return new SDFReferencesResolver();
}
/**
* CSS styles filtering.
*/
@Override
public StylesFilter createAuthorStylesFilter() {
return new SDFStylesFilter();
}
/**
* Provider for table cell span informations.
*/
@Override
public AuthorTableCellSpanProvider createAuthorTableCellSpanProvider() {
return new TableCellSpanProvider();
}
/**
* Table column width provider responsible of handling modifications regarding
* table width and column widths.
*/
@Override
public AuthorTableColumnWidthProvider createAuthorTableColumnWidthProvider() {
return new TableColumnWidthProvider();
}
/**
* Editing support for SDF documents responsible of handling typing and paste events inside section and tables.
*/
@Override
public AuthorSchemaAwareEditingHandler getAuthorSchemaAwareEditingHandler() {
return new SDFSchemaAwareEditingHandler();
}
/**
* Author Outline customizer used for custom filtering and nodes rendering in the Outline.
*/
@Override
public AuthorOutlineCustomizer createAuthorOutlineCustomizer() {
return new SDFAuthorOutlineCustomizer();
}
/**
* Simple Document Framework Author customizer used for custom nodes rendering
* in the Breadcrumb.
*/
@Override
public AuthorBreadCrumbCustomizer createAuthorBreadCrumbCustomizer() {
return new SDFAuthorBreadCrumbCustomizer();
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createExternalObjectInsertionHandler()
*/
@Override
public AuthorExternalObjectInsertionHandler createExternalObjectInsertionHandler() {
return new SDFExternalObjectInsertionHandler();
}
/**
* The unique identifier of the Document Type.
* This identifier will be used to store custom SDF options.
*/
@Override
public String getDocumentTypeID() {
return "Simple.Document.Framework.document.type";
}
/**
* Bundle description.
*/
public String getDescription() {
return "A custom extensions bundle used for the Simple Document Framework";
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createLinkTextResolver()
*/
@Override
public LinkTextResolver createLinkTextResolver() {
return new SDFLinkTextResolver();
}
}

View File

@ -0,0 +1,72 @@
package simple.documentation.framework;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.link.InvalidLinkException;
import ro.sync.ecss.extensions.api.link.LinkTextResolver;
import ro.sync.ecss.extensions.api.node.AttrValue;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
/**
* Resolve a link and obtains a text representation. The content of the link represent the name and
* the absolute location of the referred file.
*/
public class SDFLinkTextResolver extends LinkTextResolver {
/**
* The author access.
*/
private AuthorAccess authorAccess;
/**
* Logger for logging.
*/
private static final Logger logger = LogManager.getLogger(SDFLinkTextResolver.class.getName());
/**
* @see ro.sync.ecss.extensions.api.link.LinkTextResolver#resolveReference(ro.sync.ecss.extensions.api.node.AuthorNode)
*/
@Override
public String resolveReference(AuthorNode node) throws InvalidLinkException {
String linkText = null;
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT
&& "link".equals(ro.sync.basic.xml.BasicXmlUtil.getLocalName(node.getName()))) {
AuthorElement element = (AuthorElement) node;
AuthorElement[] authorElements = element.getElementsByLocalName("ref");
if (authorElements != null && authorElements.length > 0) {
// Get the first 'ref' element from link.
AuthorElement refElem = authorElements[0];
AttrValue locationAttribute = refElem.getAttribute("location");
String locationVal = locationAttribute.getValue();
URIResolver uriResolver = authorAccess.getXMLUtilAccess().getURIResolver();
try {
Source resolve = uriResolver.resolve(locationVal, authorAccess.getEditorAccess().getEditorLocation().toString());
String systemId = resolve.getSystemId();
linkText = "[" + locationVal + "] - " + systemId;
} catch (TransformerException e) {
logger.warn(e, e);
}
}
}
return linkText;
}
/**
* @see ro.sync.ecss.extensions.api.link.LinkTextResolver#activated(ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public void activated(AuthorAccess authorAccess) {
this.authorAccess = authorAccess;
}
}

View File

@ -0,0 +1,95 @@
package simple.documentation.framework.callouts;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.text.BadLocationException;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorDocumentController;
import ro.sync.ecss.extensions.api.callouts.AuthorCalloutRenderingInformation;
import ro.sync.ecss.extensions.api.callouts.CalloutsRenderingInformationProvider;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.exml.view.graphics.Color;
/**
* Callouts rendering provider.
*
* @author marius
*/
public class CalloutsRenderingProvider extends CalloutsRenderingInformationProvider {
/**
* The author access.
*/
private final AuthorAccess authorAccess;
/**
* Constructor.
*
* @param authorAccess The author access.
*/
public CalloutsRenderingProvider(AuthorAccess authorAccess) {
this.authorAccess = authorAccess;
}
@Override
public boolean shouldRenderAsCallout(AuthorPersistentHighlight persistentHighlight) {
return true;
}
@Override
public AuthorCalloutRenderingInformation getCalloutRenderingInformation(
final AuthorPersistentHighlight hl) {
return new AuthorCalloutRenderingInformation() {
@Override
public long getTimestamp() {
return -1;
}
@Override
public String getContentFromTarget(int arg0) {
try {
AuthorDocumentController documentController = authorAccess.getDocumentController();
AuthorNode nodeAtOffset = documentController.getNodeAtOffset(hl.getEndOffset());
int startOffset = hl.getStartOffset() - nodeAtOffset.getStartOffset();
int endOffset = hl.getEndOffset() - nodeAtOffset.getEndOffset();
String textContent = nodeAtOffset.getTextContent();
return textContent.substring(startOffset, endOffset);
} catch (BadLocationException e) {
e.printStackTrace();
return null;
}
}
@Override
public String getComment(int arg0) {
return hl.getClonedProperties().get(AuthorPersistentHighlight.COMMENT_ATTRIBUTE);
}
@Override
public Color getColor() {
return Color.COLOR_DARK_YELLOW;
}
@Override
public String getCalloutType() {
return "Paragraph review";
}
@Override
public String getAuthor() {
return "para_reviewer";
}
@Override
public Map<String, String> getAdditionalData() {
LinkedHashMap<String,String> clonedProperties = hl.getClonedProperties();
clonedProperties.put("Level", "Superior");
return clonedProperties;
}
};
}
}

View File

@ -0,0 +1,63 @@
package simple.documentation.framework.callouts;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.AbstractAction;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlightActionsProvider;
import ro.sync.exml.workspace.api.editor.page.author.actions.AuthorActionsProvider;
/**
* Provides actions that are shown on the contextual menu of the callout
*/
public class SDFAuthorPersistentHighlightActionsProvider implements
AuthorPersistentHighlightActionsProvider {
/**
* The list with contextual actions for a custom callout.
*/
private List<AbstractAction> actions = new ArrayList<AbstractAction>();
/**
* The default action.
*/
private AbstractAction defaultAction;
/**
* Constructor.
*
* @param authorAccess The author access.
*/
public SDFAuthorPersistentHighlightActionsProvider(AuthorAccess authorAccess) {
AuthorActionsProvider actionsProvider = authorAccess.getEditorAccess().getActionsProvider();
Map<String, Object> authorCommonActions = actionsProvider.getAuthorCommonActions();
Set<String> keySet = authorCommonActions.keySet();
for (String key : keySet) {
if (key != null) {
if ("Edit/Add_Comment".equals(key) ||
"Edit/Edit_Comment".equals(key) ||
"Edit/Remove_Comments".equals(key)) {
actions.add((AbstractAction) authorCommonActions.get(key));
if ("Edit/Edit_Comment".equals(key)) {
defaultAction = (AbstractAction) authorCommonActions.get(key);
}
}
}
}
}
/**
* @see ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlightActionsProvider#getDefaultAction(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
*/
public AbstractAction getDefaultAction(AuthorPersistentHighlight hl) {
return defaultAction;
}
/**
* @see ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlightActionsProvider#getActions(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
*/
public List<AbstractAction> getActions(AuthorPersistentHighlight hl) {
return actions;
}
}

View File

@ -0,0 +1,69 @@
package simple.documentation.framework.extensions;
import java.io.File;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.CustomAttributeValueEditor;
import ro.sync.ecss.extensions.api.EditedAttribute;
import simple.documentation.framework.AuthorAccessProvider;
/**
* Simple Document Framework attributes value editor.
*/
public class SDFAttributesValueEditor extends CustomAttributeValueEditor {
/**
* Provides access to Author functions and components.
*/
private final AuthorAccessProvider authorAccessProvider;
/**
* Constructor.
*
* @param authorAccessProvider Provides access to Author functions and components.
*/
public SDFAttributesValueEditor(AuthorAccessProvider authorAccessProvider) {
this.authorAccessProvider = authorAccessProvider;
}
/**
* Get the value for the current attribute.
*
* @param attr The Edited attribute information.
* @param parentComponent The parent component/composite.
*/
public String getAttributeValue(EditedAttribute attr, Object parentComponent) {
String refValue = null;
AuthorAccess authorAccess = authorAccessProvider.getAuthorAccess();
if (authorAccess != null) {
File refFile = authorAccess.getWorkspaceAccess().chooseFile(
// Title
"Choose reference file",
// Extensions
null,
// Filter description
null,
// Open for save
false);
if (refFile != null) {
refValue = refFile.getAbsolutePath();
}
}
return refValue;
}
/**
* Description of the attribute value editor.
*/
@Override
public String getDescription() {
return "Reference attribute value editor";
}
/**
* Filters the attributes that it handles.
*/
@Override
public boolean shouldHandleAttribute(EditedAttribute attribute) {
return attribute.getAttributeQName().endsWith("ref");
}
}

View File

@ -0,0 +1,60 @@
package simple.documentation.framework.extensions;
import javax.swing.JPopupMenu;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.ecss.extensions.api.structure.AuthorBreadCrumbCustomizer;
import ro.sync.ecss.extensions.api.structure.RenderingInformation;
/**
* Simple Document Framework Author customizer used for custom nodes rendering
* in the Breadcrumb.
*/
public class SDFAuthorBreadCrumbCustomizer extends AuthorBreadCrumbCustomizer {
/**
* For every node render its name in the Outline (with the first letter capitalized).
* Set 'Paragraph' as render for 'para' nodes
*/
@Override
public void customizeRenderingInformation(RenderingInformation renderInfo) {
// Get the node to be rendered
AuthorNode node = renderInfo.getNode();
String name = node.getName();
if (name.length() > 0) {
if ("para".equals(name)) {
name = "Paragraph";
} else {
if(renderInfo.getNode().getName().equals("customcol")) {
// Do not display the customcol elements in the BreadCrumb
name = null;
} else {
// Capitalize the first letter
name = name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
}
}
}
if (name == null) {
renderInfo.setIgnoreNodeFromDisplay(true);
} else {
// Set the render text
renderInfo.setRenderedText("[" + name + "]");
}
}
/**
* Don't show the popUp menu for the bread crumb.
*/
@Override
public void customizePopUpMenu(Object popUp, AuthorAccess authorAccess) {
if (popUp instanceof JPopupMenu) {
JPopupMenu popupMenu = (JPopupMenu)popUp;
// Remove all the components from the popUp menu .
popupMenu.removeAll();
}
}
}

View File

@ -0,0 +1,374 @@
package simple.documentation.framework.extensions;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorCaretListener;
import ro.sync.ecss.extensions.api.AuthorExtensionStateListener;
import ro.sync.ecss.extensions.api.AuthorListener;
import ro.sync.ecss.extensions.api.AuthorMouseListener;
import ro.sync.ecss.extensions.api.OptionChangedEvent;
import ro.sync.ecss.extensions.api.OptionListener;
import ro.sync.ecss.extensions.api.callouts.AuthorCalloutsController;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight.PersistentHighlightType;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
import ro.sync.ecss.extensions.api.highlights.ColorHighlightPainter;
import ro.sync.ecss.extensions.api.highlights.HighlightPainter;
import ro.sync.ecss.extensions.api.highlights.PersistentHighlightRenderer;
import ro.sync.ecss.extensions.api.structure.AuthorPopupMenuCustomizer;
import ro.sync.exml.view.graphics.Color;
import ro.sync.exml.workspace.api.Platform;
import ro.sync.exml.workspace.api.editor.page.author.actions.AuthorActionsProvider;
import ro.sync.basic.util.URLUtil;
import simple.documentation.framework.callouts.CalloutsRenderingProvider;
import simple.documentation.framework.callouts.SDFAuthorPersistentHighlightActionsProvider;
import simple.documentation.framework.filters.SDFDocumentFilter;
import simple.documentation.framework.listeners.SDFAuthorCaretListener;
import simple.documentation.framework.listeners.SDFAuthorListener;
import simple.documentation.framework.listeners.SDFAuthorMouseListener;
import simple.documentation.framework.operations.highlight.HighlightProperties;
/**
* Simple Document Framework state listener used to register custom listeners(caret listener, mouse
* listener, document listener and option listener) when the framework is activated.
*
*/
public class SDFAuthorExtensionStateListener implements AuthorExtensionStateListener {
/**
* The key used to store a custom option.
*/
private String customKey = "sdf.custom.option.key";
/**
* Custom caret listener to be added on activate and removed on deactivate
*/
private AuthorCaretListener sdfCaretListener;
/**
* Custom mouse listener
*/
private AuthorMouseListener sdfMouseListener;
/**
* Option listener to be added in the option storage.
*/
private OptionListener sdfOptionListener;
/**
* Custom author listener
*/
private AuthorListener sdfAuthorDocumentListener;
/**
* The access to the author functions.
*/
private AuthorAccess authorAccess;
/**
* Map between author name and the corresponding highlight/colors association
*/
public static Map<String, Map<PersistentHighlightType, HighlightColors>> authorHighlightColors = null;
/**
* Defines the color for a highlight
*/
public static class HighlightColors {
private Color bgColor;
private Color decorationColor;
/**
* @param bgColor The background color.
* @param decorationColor The color used for decoration
*/
public HighlightColors(Color bgColor, Color decorationColor) {
super();
this.bgColor = bgColor;
this.decorationColor = decorationColor;
}
/**
* @return Returns the bgColor.
*/
public Color getBgColor() {
return bgColor;
}
/**
* @return Returns the decorationColor.
*/
public Color getDecorationColor() {
return decorationColor;
}
}
static {
authorHighlightColors = new HashMap<String, Map<PersistentHighlightType, HighlightColors>>();
// Set colors for Author_1
Map<PersistentHighlightType, HighlightColors> colorsMap = new HashMap<PersistentHighlightType, HighlightColors>();
// Set colors for Insert Change highlight
colorsMap.put(PersistentHighlightType.CHANGE_INSERT,
new HighlightColors(new Color(230, 255, 230), new Color(130, 255, 130)));
// Set colors for Delete Change highlight
colorsMap.put(PersistentHighlightType.CHANGE_DELETE,
new HighlightColors(new Color(255, 255, 230), new Color(255, 255, 130)));
authorHighlightColors.put("Author_1", colorsMap);
// Set colors for Author_2
colorsMap = new HashMap<PersistentHighlightType, HighlightColors>();
// Set colors for Insert Change highlight
colorsMap.put(PersistentHighlightType.CHANGE_INSERT,
new HighlightColors(new Color(255, 255, 230), new Color(255, 255, 130)));
// Set colors for Delete Change highlight
colorsMap.put(PersistentHighlightType.CHANGE_DELETE,
new HighlightColors(new Color(240, 240, 240), new Color(64, 64, 64)));
authorHighlightColors.put("Author_2", colorsMap);
}
/**
* The SDF Author extension is activated.
*/
public void activated(final AuthorAccess authorAccess) {
this.authorAccess = authorAccess;
sdfOptionListener = new OptionListener(customKey) {
@Override
public void optionValueChanged(OptionChangedEvent newValue) {
// The custom option changed.
}
};
// Add document filter.
authorAccess.getDocumentController().setDocumentFilter(new SDFDocumentFilter(authorAccess));
// Add an option listener.
authorAccess.getOptionsStorage().addOptionListener(sdfOptionListener);
// Add author document listeners.
sdfAuthorDocumentListener = new SDFAuthorListener(authorAccess);
authorAccess.getDocumentController().addAuthorListener(sdfAuthorDocumentListener);
if (authorAccess.getWorkspaceAccess().getPlatform() != Platform.WEBAPP) {
// Add mouse listener.
sdfMouseListener = new SDFAuthorMouseListener(authorAccess);
authorAccess.getEditorAccess().addAuthorMouseListener(sdfMouseListener);
// Add custom tooltip
String tooltip = "[SDF] " + URLUtil.getDescription(authorAccess.getEditorAccess().getEditorLocation());
authorAccess.getEditorAccess().setEditorTabTooltipText(tooltip);
// Add caret listener.
sdfCaretListener = new SDFAuthorCaretListener(authorAccess);
//authorAccess.getEditorAccess().addAuthorCaretListener(sdfCaretListener);
// Use the actions provider to switch to "No Tags" mode.
AuthorActionsProvider actionsProvider = authorAccess.getEditorAccess().getActionsProvider();
Map<String, Object> authorCommonActions = actionsProvider.getAuthorCommonActions();
// Switch to no tags Author/No_tags
actionsProvider.invokeAction(authorCommonActions.get("Author/No_tags"));
// Set highlights tool tip and painter
authorAccess.getEditorAccess().getPersistentHighlighter().setHighlightRenderer(new PersistentHighlightRenderer() {
public String getTooltip(AuthorPersistentHighlight highlight) {
// Get highlight properties
Map<String, String> properties = highlight.getClonedProperties();
String highlightID = properties.get(HighlightProperties.ID);
String highlightAuthor = properties.get(HighlightProperties.AUTHOR);
String highlightComment = properties.get(HighlightProperties.COMMENT);
StringBuilder tooltip = new StringBuilder();
// Add create date
if (highlightID != null) {
try {
tooltip.append("Id: ").append(highlightID).append("\n");
} catch (NumberFormatException e) {
// Wrong date
}
}
// Add author
if (highlightAuthor != null) {
tooltip.append("Author: ").append(highlightAuthor).append("\n");;
}
// Add comment
if (highlightComment != null) {
tooltip.append("Comment: ").append(highlightComment);
}
return tooltip.toString();
}
public HighlightPainter getHighlightPainter(AuthorPersistentHighlight highlight) {
ColorHighlightPainter painter = new ColorHighlightPainter();
painter.setBgColor(new Color(0, 0, 255, 20));
return painter;
}
});
// Set reviews tool tip and painters
authorAccess.getReviewController().setReviewRenderer(new PersistentHighlightRenderer() {
/**
* @see ro.sync.ecss.extensions.api.highlights.PersistentHighlightRenderer#getTooltip(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
*/
public String getTooltip(AuthorPersistentHighlight highlight) {
String tooltip = "Review Tooltip";
PersistentHighlightType type = highlight.getType();
if (type == PersistentHighlightType.CHANGE_DELETE) {
// Delete highlight
tooltip = "Deleted by " + highlight.getClonedProperties().get("author");
} else if (type == PersistentHighlightType.CHANGE_INSERT) {
// Insert highlight
tooltip = "Inserted by " + highlight.getClonedProperties().get("author");
} else if (type == PersistentHighlightType.COMMENT) {
// Comment highlight
// If a null value is returned, the default tooltip text will be used
tooltip = null;
}
return tooltip;
}
/**
* @see ro.sync.ecss.extensions.api.highlights.PersistentHighlightRenderer#getHighlightPainter(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
*/
public HighlightPainter getHighlightPainter(AuthorPersistentHighlight highlight) {
HighlightPainter reviewPainter = null;
Map<String, String> properties = highlight.getClonedProperties();
String authorName = properties.get("author");
if (authorName != null) {
// Get the highlight/colors association for this author name
Map<PersistentHighlightType, HighlightColors> highlightColorsMap = authorHighlightColors.get(authorName);
if (highlightColorsMap != null) {
// Get the associated colors for this type of highlight
HighlightColors highlightColors = highlightColorsMap.get(highlight.getType());
if (highlightColors != null) {
reviewPainter = new ColorHighlightPainter();
// Background color
((ColorHighlightPainter) reviewPainter).setBgColor(highlightColors.getBgColor());
// Decoration color
((ColorHighlightPainter) reviewPainter).setColor(highlightColors.getDecorationColor());
}
}
}
// If a null value is returned the default highlight painter is used.
return reviewPainter;
}
});
// Get the list with all track changes highlights from the document
AuthorPersistentHighlight[] changeHighlights = authorAccess.getReviewController().getChangeHighlights();
if(changeHighlights.length > 0 && !authorAccess.getReviewController().isTrackingChanges()) {
// If the document has track changes highlights, then set track changes mode to ON
authorAccess.getReviewController().toggleTrackChanges();
}
// Add a popup menu customizer. Group Cut, Copy, Paste and Paste as XML actions in Edit menu.
authorAccess.getEditorAccess().addPopUpMenuCustomizer(new AuthorPopupMenuCustomizer() {
// Customize popup menu
@SuppressWarnings("serial")
public void customizePopUpMenu(Object popUp, final AuthorAccess authorAccess) {
JPopupMenu popupMenu = (JPopupMenu) popUp;
Component[] components = popupMenu.getComponents();
// The new 'Edit' menu
JMenu editMenu = new JMenu("Edit");
boolean shouldAddEditMenu = false;
for (int i = 0; i < components.length; i++) {
if (components[i] instanceof JMenuItem) {
JMenuItem menuItem = (JMenuItem) components[i];
if (menuItem.getAction() != null) {
Object name = menuItem.getAction().getValue(Action.NAME);
if ("Cut".equals(name) || "Copy".equals(name) || "Paste".equals(name)|| "Paste as XML".equals(name)) {
// Remove edit actions
popupMenu.remove(menuItem);
// Add edit actions to edit menu
editMenu.add(menuItem);
shouldAddEditMenu = true;
}
}
}
}
if (shouldAddEditMenu) {
popupMenu.add(editMenu, 0);
}
final URL selectedUrl;
try {
final String selectedText = authorAccess.getEditorAccess().getSelectedText();
if (selectedText != null) {
selectedUrl = new URL(selectedText);
// Open selected url in system application
popupMenu.add(new JMenuItem(new AbstractAction("Open in system application") {
public void actionPerformed(ActionEvent e) {
authorAccess.getWorkspaceAccess().openInExternalApplication(selectedUrl, true);
}
}), 0);
}
} catch (MalformedURLException e2) {}
}
});
// Add a provider for callouts rendering
CalloutsRenderingProvider calloutsRenderingProvider = new CalloutsRenderingProvider(authorAccess);
AuthorCalloutsController authorCalloutsController =
authorAccess.getReviewController().getAuthorCalloutsController();
authorCalloutsController.setCalloutsRenderingInformationProvider(calloutsRenderingProvider);
// Show insertions callouts
authorCalloutsController.setShowInsertionsCallouts(true);
// Add an actions provider for actions that are shown on the contextual menu of the
// custom callout
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
SDFAuthorPersistentHighlightActionsProvider highlightActionsProvider =
new SDFAuthorPersistentHighlightActionsProvider(authorAccess);
highlighter.setHighlightsActionsProvider(highlightActionsProvider);
}
// Other custom initializations...
}
/**
* The SDF Author extension is deactivated.
*/
public void deactivated(AuthorAccess authorAccess) {
// Remove the option listener.
authorAccess.getOptionsStorage().removeOptionListener(sdfOptionListener);
// Remove document listeners.
authorAccess.getDocumentController().removeAuthorListener(sdfAuthorDocumentListener);
if (authorAccess.getWorkspaceAccess().getPlatform() != Platform.WEBAPP) {
// Remove mouse listener.
authorAccess.getEditorAccess().removeAuthorMouseListener(sdfMouseListener);
// Remove caret listener.
authorAccess.getEditorAccess().removeAuthorCaretListener(sdfCaretListener);
}
// Other actions...
}
public String getDescription() {
return "Simple Document Framework state listener";
}
/**
* Returns the Author access.
*
* @return The access to Author functions and components.
*/
public AuthorAccess getAuthorAccess() {
return authorAccess;
}
}

View File

@ -0,0 +1,158 @@
package simple.documentation.framework.extensions;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.text.BadLocationException;
import javax.swing.tree.TreePath;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.ecss.extensions.api.structure.AuthorOutlineCustomizer;
import ro.sync.ecss.extensions.api.structure.RenderingInformation;
/**
* Simple Document Framework Author Outline customizer used for custom filtering and nodes rendering
* in the Outline.
*/
public class SDFAuthorOutlineCustomizer extends AuthorOutlineCustomizer {
/**
* Ignore 'b' nodes.
*/
@Override
public boolean ignoreNode(AuthorNode node) {
if (node.getName().equals("b")) {
return true;
}
// Default behavior
return false;
}
/**
* For every node render its name in the Outline (with the first letter capitalized).
* Set 'Paragraph' as render for 'para' nodes
* Render the title text for 'section' nodes and add 'Section' as additional info and tooltip.
*/
@Override
public void customizeRenderingInformation(
RenderingInformation renderInfo) {
// Get the node to be rendered
AuthorNode node = renderInfo.getNode();
String name = node.getName();
if (name.length() > 0) {
if ("para".equals(name)) {
name = "Paragraph";
} else {
// Capitalize the first letter
name = name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
}
}
// Render additional attribute value
String additionalAttrValue = renderInfo.getAdditionalRenderedAttributeValue();
if (additionalAttrValue != null && additionalAttrValue.length() > 0) {
renderInfo.setAdditionalRenderedAttributeValue("[" + additionalAttrValue + "]");
}
// Set the render text
renderInfo.setRenderedText("[" + name + "]");
// Render the title text in the 'section' additional info.
if (node.getName().equals("section")) {
if (node instanceof AuthorElement) {
// Get the 'section' content nodes
List<AuthorNode> contentNodes = ((AuthorElement) node).getContentNodes();
if (contentNodes.size() > 0) {
AuthorNode authorNode = contentNodes.get(0);
if ("title".equals(authorNode.getName())) {
try {
// Render the title
String textContent = authorNode.getTextContent();
if (textContent != null && textContent.trim().length() > 0) {
if (textContent.length() > 20) {
textContent = textContent.substring(0, 20);
}
renderInfo.setRenderedText(textContent);
// Set 'Section' as additional info
renderInfo.setAdditionalRenderedText("[Section]");
// Set the tooltip text
renderInfo.setTooltipText(textContent);
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* Customize the popUp menu.
* If the selected node is a 'title' from 'section' remove the 'Delete'
* action from the popUp menu and add the 'Remove content' action if the selection
* is single.
*/
@Override
public void customizePopUpMenu(Object popUp, final AuthorAccess authorAccess) {
if (authorAccess != null) {
if (popUp instanceof JPopupMenu) {
JPopupMenu popupMenu = (JPopupMenu)popUp;
// Find the selected paths from the Outliner
TreePath[] selectedPaths = authorAccess.getOutlineAccess().getSelectedPaths(true);
for (TreePath treePath : selectedPaths) {
Object[] authorNodesPath = treePath.getPath();
if (authorNodesPath.length > 0) {
// Find the selected node (last node from the list of path nodes)
final AuthorNode selectedAuthorNode = (AuthorNode) authorNodesPath[authorNodesPath.length - 1];
// Find if the current selected node is a title from section
if ("title".equals(selectedAuthorNode.getName()) &&
"section".equals(selectedAuthorNode.getParent().getName())) {
// Find the popUp item corresponding to the 'Delete' action
Component[] components = popupMenu.getComponents();
int deleteItemIndex = 0;
for (int i = 0; i < components.length; i++) {
if (components[i] instanceof JMenuItem) {
String itemText = ((JMenuItem) components[i]).getText();
if ("Delete".equals(itemText)) {
// The 'Remove Content' item will be added at the same index
deleteItemIndex = i;
// Remove the 'Delete' action
popupMenu.remove(deleteItemIndex);
}
}
}
if (selectedPaths.length == 1) {
// Add the 'Remove content' action
JMenuItem sdfMenuItem = new JMenuItem("Remove Content");
sdfMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// Remove title content
int startOffset = selectedAuthorNode.getStartOffset();
int endOffset = selectedAuthorNode.getEndOffset();
if (endOffset > startOffset + 1) {
authorAccess.getDocumentController().delete(
startOffset + 1,
endOffset);
}
}
});
// Add the menu item in the menu
popupMenu.add(sdfMenuItem, deleteItemIndex);
}
break;
}
}
}
}
}
}
}

View File

@ -0,0 +1,72 @@
package simple.documentation.framework.extensions;
import java.net.URL;
import java.util.List;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler;
import ro.sync.ecss.extensions.api.AuthorOperationException;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.basic.util.URLUtil;
/**
* Accepts insertion of images and XML files from external sources.
*/
public class SDFExternalObjectInsertionHandler extends AuthorExternalObjectInsertionHandler {
/**
* @see ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler#insertURLs(ro.sync.ecss.extensions.api.AuthorAccess, java.util.List, int)
*/
@Override
public void insertURLs(AuthorAccess authorAccess, List<URL> urls, int source)
throws AuthorOperationException {
for (URL url : urls) {
String xmlFragment = null;
if (authorAccess.getUtilAccess().isSupportedImageURL(url)) {
// Insert an image element
xmlFragment = "<image href=\"" + url.toString() + "\"/>";
} else {
// Insert
xmlFragment = "<ref location=\"" + url.toString() + "\"/>";
}
if (xmlFragment != null) {
// Create am image fragment
AuthorDocumentFragment imageFragment = authorAccess.getDocumentController().createNewDocumentFragmentInContext(
xmlFragment, authorAccess.getEditorAccess().getCaretOffset());
// Insert the fragment
authorAccess.getDocumentController().insertFragment(authorAccess.getEditorAccess().getCaretOffset(), imageFragment);
}
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler#acceptSource(ro.sync.ecss.extensions.api.AuthorAccess, int)
*/
@Override
public boolean acceptSource(AuthorAccess authorAccess, int source) {
if (source == DND_EXTERNAL) {
// Accept only from external source
return true;
}
// For urls from other sources (Dita Maps Manager, Project) a new tab will be open.
return false;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler#acceptURLs(ro.sync.ecss.extensions.api.AuthorAccess, java.util.List, int)
*/
@Override
public boolean acceptURLs(AuthorAccess authorAccess, List<URL> urls, int source) {
boolean accept = acceptSource(authorAccess, source);
if (accept) {
for (int i = 0; i < urls.size(); i++) {
// Accept only XML and image files
if (!authorAccess.getUtilAccess().isSupportedImageURL(urls.get(i)) &&
!"xml".equalsIgnoreCase(URLUtil.getExtension(urls.get(i).toString()))) {
accept = false;
break;
}
}
}
return accept;
}
}

View File

@ -0,0 +1,199 @@
package simple.documentation.framework.extensions;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.transform.sax.SAXSource;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
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;
/**
* References resolver for Simple Documentation Framework.
*
*/
public class SDFReferencesResolver implements AuthorReferenceResolver {
/**
* Logger for logging.
*/
private static Logger logger = LogManager.getLogger(
SDFReferencesResolver.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();
if (attrStringVal.length() > 0) {
try {
URL absoluteUrl = new URL(new URL(systemID),
authorAccess.getUtilAccess().correctURL(attrStringVal));
InputSource inputSource = entityResolver.resolveEntity(null,
absoluteUrl.toString());
if(inputSource == null) {
inputSource = new InputSource(absoluteUrl.toString());
}
XMLReader xmlReader = authorAccess.getXMLUtilAccess().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.getUtilAccess().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";
}
}

View File

@ -0,0 +1,353 @@
package simple.documentation.framework.extensions;
import java.util.List;
import javax.swing.text.BadLocationException;
import ro.sync.contentcompletion.xml.ContextElement;
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorOperationException;
import ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandlerAdapter;
import ro.sync.ecss.extensions.api.AuthorSchemaManager;
import ro.sync.ecss.extensions.api.InvalidEditException;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
/**
* Specific editing support for SDF documents. Handles typing and paste events inside section and tables.
*/
public class SDFSchemaAwareEditingHandler extends AuthorSchemaAwareEditingHandlerAdapter {
private static final String SDF_NAMESPACE = "http://www.oxygenxml.com/sample/documentation";
/**
* SDF table element name.
*/
private static final String SDF_TABLE = "table";
/**
* SDF table row name.
*/
private static final String SDF_TABLE_ROW = "tr";
/**
* SDF table cell name.
*/
private static final String SDF_TABLE_CELL = "td";
/**
* SDF section element name.
*/
private static final String SECTION = "section";
/**
* SDF para element name.
*/
protected static final String PARA = "para";
/**
* SDF title element name.
*/
protected static final String TITLE = "title";
/**
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleDelete(int, int, ro.sync.ecss.extensions.api.AuthorAccess, boolean)
*/
@Override
public boolean handleDelete(int offset, int deleteType, AuthorAccess authorAccess, boolean wordLevel)
throws InvalidEditException {
// Not handled.
return false;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleDeleteElementTags(ro.sync.ecss.extensions.api.node.AuthorNode, ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public boolean handleDeleteElementTags(AuthorNode nodeToUnwrap, AuthorAccess authorAccess)
throws InvalidEditException {
// Not handled.
return false;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleDeleteSelection(int, int, int, ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public boolean handleDeleteSelection(int selectionStart, int selectionEnd, int generatedByActionId,
AuthorAccess authorAccess) throws InvalidEditException {
// Not handled.
return false;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleJoinElements(ro.sync.ecss.extensions.api.node.AuthorNode, java.util.List, ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public boolean handleJoinElements(AuthorNode targetNode, List<AuthorNode> nodesToJoin, AuthorAccess authorAccess)
throws InvalidEditException {
// Not handled.
return false;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handlePasteFragment(int, ro.sync.ecss.extensions.api.node.AuthorDocumentFragment[], int, ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public boolean handlePasteFragment(int offset, AuthorDocumentFragment[] fragmentsToInsert, int actionId,
AuthorAccess authorAccess) throws InvalidEditException {
boolean handleInsertionEvent = false;
AuthorSchemaManager authorSchemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
if (!authorSchemaManager.isLearnSchema() &&
!authorSchemaManager.hasLoadingErrors() &&
authorSchemaManager.getAuthorSchemaAwareOptions().isEnableSmartPaste()) {
handleInsertionEvent = handleInsertionEvent(offset, fragmentsToInsert, authorAccess);
}
return handleInsertionEvent;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleTyping(int, char, ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public boolean handleTyping(int offset, char ch, AuthorAccess authorAccess)
throws InvalidEditException {
boolean handleTyping = false;
AuthorSchemaManager authorSchemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
if (!authorSchemaManager.isLearnSchema() &&
!authorSchemaManager.hasLoadingErrors() &&
authorSchemaManager.getAuthorSchemaAwareOptions().isEnableSmartTyping()) {
try {
AuthorDocumentFragment characterFragment =
authorAccess.getDocumentController().createNewDocumentTextFragment(String.valueOf(ch));
handleTyping = handleInsertionEvent(offset, new AuthorDocumentFragment[] {characterFragment}, authorAccess);
} catch (AuthorOperationException e) {
throw new InvalidEditException(e.getMessage(), "Invalid typing event: " + e.getMessage(), e, false);
}
}
return handleTyping;
}
/**
* Handle an insertion event (either typing or paste).
*
* @param offset Offset where the insertion event occurred.
* @param fragmentsToInsert Fragments that must be inserted at the given offset.
* @param authorAccess Author access.
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
*
* @throws InvalidEditException The event was rejected because it is invalid.
*/
private boolean handleInsertionEvent(
int offset,
AuthorDocumentFragment[] fragmentsToInsert,
AuthorAccess authorAccess) throws InvalidEditException {
AuthorSchemaManager authorSchemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
boolean handleEvent = false;
try {
AuthorNode nodeAtInsertionOffset = authorAccess.getDocumentController().getNodeAtOffset(offset);
if (isElementWithNameAndNamespace(nodeAtInsertionOffset, SDF_TABLE)) {
// Check if the fragment is allowed as it is.
boolean canInsertFragments = authorSchemaManager.canInsertDocumentFragments(
fragmentsToInsert,
offset,
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS);
if (!canInsertFragments) {
handleEvent = handleInvalidInsertionEventInTable(
offset,
fragmentsToInsert,
authorAccess,
authorSchemaManager);
}
} else if(isElementWithNameAndNamespace(nodeAtInsertionOffset, SECTION)) {
// Check if the fragment is allowed as it is.
boolean canInsertFragments = authorSchemaManager.canInsertDocumentFragments(
fragmentsToInsert,
offset,
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS);
if (!canInsertFragments) {
// Insertion in 'section' element
handleEvent = handleInvalidInsertionEventInSect(
offset,
fragmentsToInsert,
authorAccess,
authorSchemaManager);
}
}
} catch (BadLocationException e) {
throw new InvalidEditException(e.getMessage(), "Invalid typing event: " + e.getMessage(), e, false);
} catch (AuthorOperationException e) {
throw new InvalidEditException(e.getMessage(), "Invalid typing event: " + e.getMessage(), e, false);
}
return handleEvent;
}
/**
* @return <code>true</code> if the given node is an element with the given local name and from the SDF namespace.
*/
protected boolean isElementWithNameAndNamespace(AuthorNode node, String elementLocalName) {
boolean result = false;
if(node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
AuthorElement element = (AuthorElement) node;
result = elementLocalName.equals(element.getLocalName()) && element.getNamespace().equals(SDF_NAMESPACE);
}
return result;
}
/**
* Try to handle invalid insertion events in a SDF 'table'.
* A row element will be inserted with a new cell in which the fragments will be inserted.
*
* @param offset Offset where the insertion event occurred.
* @param fragmentsToInsert Fragments that must be inserted at the given offset.
* @param authorAccess Author access.
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
*/
private boolean handleInvalidInsertionEventInTable(
int offset,
AuthorDocumentFragment[] fragmentsToInsert,
AuthorAccess authorAccess,
AuthorSchemaManager authorSchemaManager) throws BadLocationException, AuthorOperationException {
boolean handleEvent = false;
// Typing/paste inside a SDF table. We will try to wrap the fragment into a new cell and insert it inside a new row.
WhatElementsCanGoHereContext context = authorSchemaManager.createWhatElementsCanGoHereContext(offset);
StringBuilder xmlFragment = new StringBuilder("<");
xmlFragment.append(SDF_TABLE_ROW);
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
}
xmlFragment.append("/>");
// Check if a row can be inserted at the current offset.
boolean canInsertRow = authorSchemaManager.canInsertDocumentFragments(
new AuthorDocumentFragment[] {authorAccess.getDocumentController().createNewDocumentFragmentInContext(xmlFragment.toString(), offset)},
context,
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS);
// Derive the context by adding a new row element with a cell.
if (canInsertRow) {
pushContextElement(context, SDF_TABLE_ROW);
pushContextElement(context, SDF_TABLE_CELL);
// Test if fragments can be inserted in the new context.
if (authorSchemaManager.canInsertDocumentFragments(
fragmentsToInsert,
context,
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS)) {
// Insert a new row with a cell.
xmlFragment = new StringBuilder("<");
xmlFragment.append(SDF_TABLE_ROW);
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
}
xmlFragment.append("><");
xmlFragment.append(SDF_TABLE_CELL);
xmlFragment.append("/></");
xmlFragment.append(SDF_TABLE_ROW);
xmlFragment.append(">");
authorAccess.getDocumentController().insertXMLFragment(xmlFragment.toString(), offset);
// Get the newly inserted cell.
AuthorNode newCell = authorAccess.getDocumentController().getNodeAtOffset(offset + 2);
for (int i = 0; i < fragmentsToInsert.length; i++) {
authorAccess.getDocumentController().insertFragment(newCell.getEndOffset(), fragmentsToInsert[i]);
}
handleEvent = true;
}
}
return handleEvent;
}
/**
* Derive the given context by adding the specified element.
*/
protected void pushContextElement(WhatElementsCanGoHereContext context, String elementName) {
ContextElement contextElement = new ContextElement();
contextElement.setQName(elementName);
contextElement.setNamespace(SDF_NAMESPACE);
context.pushContextElement(contextElement, null);
}
/**
* Try to handle invalid insertion events in 'section'.
* The solution is to insert the <code>fragmentsToInsert</code> into a 'title' element if the sect element is empty or
* into a 'para' element if the sect already contains a 'title'.
*
* @param offset Offset where the insertion event occurred.
* @param fragmentsToInsert Fragments that must be inserted at the given offset.
* @param authorAccess Author access.
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
*/
private boolean handleInvalidInsertionEventInSect(int offset, AuthorDocumentFragment[] fragmentsToInsert, AuthorAccess authorAccess,
AuthorSchemaManager authorSchemaManager) throws BadLocationException, AuthorOperationException {
boolean handleEvent = false;
// Typing/paste inside an section.
AuthorElement sectionElement = (AuthorElement) authorAccess.getDocumentController().getNodeAtOffset(offset);
if (sectionElement.getStartOffset() + 1 == sectionElement.getEndOffset()) {
// Empty section element
WhatElementsCanGoHereContext context = authorSchemaManager.createWhatElementsCanGoHereContext(offset);
// Derive the context by adding a title.
pushContextElement(context, TITLE);
// Test if fragments can be inserted in 'title' element
if (authorSchemaManager.canInsertDocumentFragments(
fragmentsToInsert,
context,
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS)) {
// Create a title structure and insert fragments inside
StringBuilder xmlFragment = new StringBuilder("<").append(TITLE);
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
}
xmlFragment.append(">").append("</").append(TITLE).append(">");
// Insert title
authorAccess.getDocumentController().insertXMLFragment(xmlFragment.toString(), offset);
// Insert fragments
AuthorNode newParaNode = authorAccess.getDocumentController().getNodeAtOffset(offset + 1);
for (int i = 0; i < fragmentsToInsert.length; i++) {
authorAccess.getDocumentController().insertFragment(newParaNode.getEndOffset(), fragmentsToInsert[i]);
}
handleEvent = true;
}
} else {
// Check if there is just a title.
List<AuthorNode> contentNodes = sectionElement.getContentNodes();
if (contentNodes.size() == 1) {
AuthorNode child = contentNodes.get(0);
boolean isTitleChild = isElementWithNameAndNamespace(child, TITLE);
if (isTitleChild && child.getEndOffset() < offset) {
// We are after the title.
// Empty sect element
WhatElementsCanGoHereContext context = authorSchemaManager.createWhatElementsCanGoHereContext(offset);
// Derive the context by adding a para
pushContextElement(context, PARA);
// Test if fragments can be inserted in 'para' element
if (authorSchemaManager.canInsertDocumentFragments(
fragmentsToInsert,
context,
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS)) {
// Create a para structure and insert fragments inside
StringBuilder xmlFragment = new StringBuilder("<").append(PARA);
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
}
xmlFragment.append(">").append("</").append(PARA).append(">");
// Insert para
authorAccess.getDocumentController().insertXMLFragment(xmlFragment.toString(), offset);
// Insert fragments
AuthorNode newParaNode = authorAccess.getDocumentController().getNodeAtOffset(offset + 1);
for (int i = 0; i < fragmentsToInsert.length; i++) {
authorAccess.getDocumentController().insertFragment(newParaNode.getEndOffset(), fragmentsToInsert[i]);
}
handleEvent = true;
}
}
}
}
return handleEvent;
}
}

View File

@ -0,0 +1,142 @@
package simple.documentation.framework.extensions;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import ro.sync.contentcompletion.xml.CIAttribute;
import ro.sync.contentcompletion.xml.CIElement;
import ro.sync.contentcompletion.xml.CIValue;
import ro.sync.contentcompletion.xml.Context;
import ro.sync.contentcompletion.xml.ContextElement;
import ro.sync.contentcompletion.xml.SchemaManagerFilter;
import ro.sync.contentcompletion.xml.WhatAttributesCanGoHereContext;
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
import ro.sync.contentcompletion.xml.WhatPossibleValuesHasAttributeContext;
import simple.documentation.framework.SDFElement;
/**
* Schema manager filter for the Simple Documentation Framework.
* Filters custom elements, attributes, elements values and attributes values.
*
*/
public class SDFSchemaManagerFilter implements SchemaManagerFilter {
/**
* Filter values of the "href" attribute of an image element.
*/
public List<CIValue> filterAttributeValues(List<CIValue> attributeValues,
WhatPossibleValuesHasAttributeContext context) {
// If the element from the current context is the "image" element and the current context
// attribute is "href" then add a custom URL value to the list of default content completion
// proposals.
if (context != null) {
ContextElement currentElement = context.getParentElement();
String attributeName = context.getAttributeName();
if("image".equals(currentElement.getQName()) && "href".equals(attributeName)) {
CIValue newValue = new CIValue("Custom_image_URL");
if (attributeValues == null) {
attributeValues = new ArrayList<CIValue>();
}
// Add the new value.
attributeValues.add(newValue);
}
}
return attributeValues;
}
/**
* Filter attributes of the "table" element.
*/
public List<CIAttribute> filterAttributes(List<CIAttribute> attributes,
WhatAttributesCanGoHereContext context) {
// If the element from the current context is the 'table' element add the
// attribute named 'frame' to the list of default content completion proposals
if (context != null) {
ContextElement contextElement = context.getParentElement();
if ("table".equals(contextElement.getQName())) {
CIAttribute frameAttribute = new CIAttribute();
frameAttribute.setName("frame");
frameAttribute.setRequired(false);
frameAttribute.setFixed(false);
frameAttribute.setDefaultValue("void");
if (attributes == null) {
attributes = new ArrayList<CIAttribute>();
}
attributes.add(frameAttribute);
}
}
return attributes;
}
/**
* Filter the value of the "title" element from the "section".
*/
public List<CIValue> filterElementValues(List<CIValue> elementValues,
Context context) {
// If the element from the current context is the title of the section element then add the
// "Custom Section Title" value to the list of default content completion proposals
if (context != null) {
Stack<ContextElement> elementStack = context.getElementStack();
if (elementStack != null) {
ContextElement contextElement = elementStack.peek();
if(contextElement != null) {
int size = elementStack.size();
if ("title".equals(contextElement.getQName())) {
ContextElement parentElement = elementStack.elementAt(size - 2);
if (parentElement != null && "section".equals(parentElement.getQName())) {
elementValues = new ArrayList<CIValue>();
CIValue val = new CIValue("Custom Section Title");
elementValues.add(val);
}
}
}
}
}
return elementValues;
}
/**
* Filter "header" elements.
*/
public List<CIElement> filterElements(List<CIElement> elements,
WhatElementsCanGoHereContext context) {
// If the element from the current context is the 'header' element remove the
// 'td' element from the list of content completion proposals and add the
// 'th' element.
if (context != null) {
Stack<ContextElement> elementStack = context.getElementStack();
if (elementStack != null) {
ContextElement contextElement = context.getElementStack().peek();
if ("header".equals(contextElement.getQName())) {
if (elements != null) {
for (Iterator<CIElement> iterator = elements.iterator(); iterator.hasNext();) {
CIElement element = iterator.next();
// Remove the 'td' element
if ("td".equals(element.getQName())) {
elements.remove(element);
break;
}
}
} else {
elements = new ArrayList<CIElement>();
}
// Insert the 'th' element in the list of content completion proposals
CIElement thElement = new SDFElement();
thElement.setName("th");
elements.add(thElement);
}
}
} else {
// If the given context is null then the given list of content completion elements contains
// global elements.
}
return elements;
}
public String getDescription() {
return "Implementation for the Simple Documentation Framework schema manager filter.";
}
}

View File

@ -0,0 +1,139 @@
package simple.documentation.framework.extensions;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import ro.sync.ecss.css.EditorContent;
import ro.sync.ecss.css.LabelContent;
import ro.sync.ecss.css.StaticContent;
import ro.sync.ecss.css.Styles;
import ro.sync.ecss.extensions.api.StylesFilter;
import ro.sync.ecss.extensions.api.editor.InplaceEditorArgumentKeys;
import ro.sync.ecss.extensions.api.node.AuthorNode;
/**
*
* Style Filter for Simple Documentation Framework.
*
*/
public class SDFStylesFilter implements StylesFilter {
/**
* Filter the style of "para", "b" and "i" elements.
*/
public Styles filter(Styles styles, AuthorNode authorNode) {
//Use fixed font size of 12 for "para".
if(authorNode.getName().equals("para")) {
ro.sync.exml.view.graphics.Font original = styles.getFont();
ro.sync.exml.view.graphics.Font modified = new ro.sync.exml.view.graphics.Font(
original.getName(), original.getStyle(), 12);
styles.setProperty(Styles.KEY_FONT, modified);
}
//Use foreground color red for "b"
if(authorNode.getName().equals("b")) {
ro.sync.exml.view.graphics.Color red = ro.sync.exml.view.graphics.Color.COLOR_RED;
styles.setProperty(Styles.KEY_FOREGROUND_COLOR, red);
}
//Use left border width of 5 pixels for "i"
if(authorNode.getName().equals("i")) {
styles.setProperty(Styles.KEY_BORDER_LEFT_WIDTH, new Integer(5));
}
if(authorNode.getType() == AuthorNode.NODE_TYPE_PSEUDO_ELEMENT
&& "before".equals(authorNode.getName())) {
authorNode = authorNode.getParent();
if ("country".equals(authorNode.getName())) {
// This is the BEFORE pseudo element of the "country" element.
// Read the supported countries from the configuration file.
// This will be a comma separated enumeration: France, Spain, Great Britain
String countries = readCountriesFromFile();
Map<String, Object> formControlArgs = new HashMap<String, Object>();
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_EDIT_QUALIFIED, "#text");
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_TYPE, InplaceEditorArgumentKeys.TYPE_COMBOBOX);
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_VALUES, countries);
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_EDITABLE, "false");
// We also add a label in form of the form control.
Map<String, Object> labelProps = new HashMap<String, Object>();
labelProps.put("text", "Country: ");
labelProps.put("styles", "* {width: 100px; color: gray;}");
StaticContent[] mixedContent = new StaticContent[] {new LabelContent(labelProps), new EditorContent(formControlArgs)};
styles.setProperty(Styles.KEY_MIXED_CONTENT, mixedContent);
}
}
// The previously added form control is the only way the element can be edited.
if ("country".equals(authorNode.getName())) {
styles.setProperty(Styles.KEY_VISIBITY, "-oxy-collapse-text");
}
return styles;
}
/**
* The filter is invoked every time the styles are needed so we will make sure
* we load the countries only once.
*/
private String countries = null;
/**
* Read the available countries from the configuration file.
*
* @return The supported countries, read from the configuration file.
* This will be a comma separated enumeration: France, Spain, Great Britain.
*/
private String readCountriesFromFile() {
if (countries == null) {
StringBuilder countriesBuilder = new StringBuilder();
// Our countries file is located in the framework folder. To compute
// the framework location we will use the fact that the JAR this class is in
// is located inside the framework folder.
String classLocation = "/" + getClass().getCanonicalName().replace(".", "/") + ".class";
URL resource = getClass().getResource(classLocation);
if (resource != null) {
// The URL for the class looks like this:
// jar:file:/D:/projects/eXml/frameworks/sdf/sdf.jar!/simple/documentation/framework/extensions/SDFStylesFilter.class
String jarURL = resource.toString();
// This is the URL of the JAR form where the class was loaded.
jarURL = jarURL.substring(jarURL.indexOf(":") + 1, jarURL.indexOf("!/"));
try {
// We know the resources are next to the JAR.
URL resourceFile = new URL(new URL(jarURL), "resources/countries.txt");
InputStream openStream = resourceFile.openStream();
try {
InputStreamReader inputStreamReader = new InputStreamReader(openStream, "UTF8");
char[] cbuf = new char[1024];
int length = -1;
while ((length = inputStreamReader.read(cbuf)) != -1) {
countriesBuilder.append(cbuf, 0, length);
}
} finally {
openStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
countries = countriesBuilder.toString();
}
return countries;
}
/**
* Description.
*/
public String getDescription() {
return "Implementation for the Simple Documentation Framework style filter.";
}
}

View File

@ -0,0 +1,74 @@
package simple.documentation.framework.extensions;
import ro.sync.ecss.extensions.api.AuthorTableCellSpanProvider;
import ro.sync.ecss.extensions.api.node.AttrValue;
import ro.sync.ecss.extensions.api.node.AuthorElement;
/**
* Simple Documentation Framework table cell span provider.
*
*/
public class TableCellSpanProvider implements AuthorTableCellSpanProvider {
/**
* Extracts the integer specifying 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 specifying 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;
}
/**
* @return true considering the column specifications always available.
*/
public boolean hasColumnSpecifications(AuthorElement tableElement) {
return true;
}
/**
* Ignored. We do not extract data from the <code>table</code> element.
*/
public void init(AuthorElement table) {
// Nothing to do.
}
public String getDescription() {
return "Implementation for the Simple Documentation Framework table layout.";
}
}

View File

@ -0,0 +1,221 @@
package simple.documentation.framework.extensions;
import java.util.ArrayList;
import java.util.List;
import ro.sync.ecss.extensions.api.AuthorDocumentController;
import ro.sync.ecss.extensions.api.AuthorOperationException;
import ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider;
import ro.sync.ecss.extensions.api.WidthRepresentation;
import ro.sync.ecss.extensions.api.node.AttrValue;
import ro.sync.ecss.extensions.api.node.AuthorElement;
/**
* Simple Documentation Framework table column width provider.
*
*/
public class TableColumnWidthProvider implements AuthorTableColumnWidthProvider {
/**
* Cols start offset
*/
private int colsStartOffset;
/**
* Cols end offset
*/
private int colsEndOffset;
/**
* Column widths specifications
*/
private List<WidthRepresentation> colWidthSpecs = new ArrayList<WidthRepresentation>();
/**
* The table element
*/
private AuthorElement tableElement;
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#commitColumnWidthModifications(ro.sync.ecss.extensions.api.AuthorDocumentController, ro.sync.ecss.extensions.api.WidthRepresentation[], java.lang.String)
*/
public void commitColumnWidthModifications(AuthorDocumentController authorDocumentController,
WidthRepresentation[] colWidths, String tableCellsTagName) throws AuthorOperationException {
if ("td".equals(tableCellsTagName)) {
if (colWidths != null && tableElement != null) {
if (colsStartOffset >= 0 && colsEndOffset >= 0 && colsStartOffset < colsEndOffset) {
authorDocumentController.delete(colsStartOffset,
colsEndOffset);
}
String xmlFragment = createXMLFragment(colWidths);
int offset = -1;
AuthorElement[] header = tableElement.getElementsByLocalName("header");
if (header != null && header.length > 0) {
// Insert the cols elements before the 'header' element
offset = header[0].getStartOffset();
} else {
AuthorElement[] title = tableElement.getElementsByLocalName("title");
if (title != null && title.length > 0) {
// Insert the cols elements after the 'title' element
offset = title[0].getStartOffset() + 1;
} else {
// Just insert after table start tag
offset = tableElement.getStartOffset() + 1;
}
}
if (offset == -1) {
throw new AuthorOperationException("No valid offset to insert the columns width specification.");
}
authorDocumentController.insertXMLFragment(xmlFragment, offset);
}
}
}
/**
* Creates the XML fragment representing the column specifications.
*
* @param widthRepresentations
* @return The XML fragment as a string.
*/
private String createXMLFragment(WidthRepresentation[] widthRepresentations) {
StringBuffer fragment = new StringBuffer();
String ns = tableElement.getNamespace();
for (int i = 0; i < widthRepresentations.length; i++) {
WidthRepresentation width = widthRepresentations[i];
fragment.append("<customcol");
String strRepresentation = width.getWidthRepresentation();
if (strRepresentation != null) {
fragment.append(" width=\"" + width.getWidthRepresentation() + "\"");
}
if (ns != null && ns.length() > 0) {
fragment.append(" xmlns=\"" + ns + "\"");
}
fragment.append("/>");
}
return fragment.toString();
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#commitTableWidthModification(ro.sync.ecss.extensions.api.AuthorDocumentController, int, java.lang.String)
*/
public void commitTableWidthModification(AuthorDocumentController authorDocumentController,
int newTableWidth, String tableCellsTagName) throws AuthorOperationException {
if ("td".equals(tableCellsTagName)) {
if (newTableWidth > 0) {
if (tableElement != null) {
String newWidth = String.valueOf(newTableWidth);
authorDocumentController.setAttribute(
"width",
new AttrValue(newWidth),
tableElement);
} else {
throw new AuthorOperationException("Cannot find the element representing the table.");
}
}
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#getCellWidth(ro.sync.ecss.extensions.api.node.AuthorElement, int, int)
*/
public List<WidthRepresentation> getCellWidth(AuthorElement cellElement, int colNumberStart,
int colSpan) {
List<WidthRepresentation> toReturn = null;
int size = colWidthSpecs.size();
if (size >= colNumberStart && size >= colNumberStart + colSpan) {
toReturn = new ArrayList<WidthRepresentation>(colSpan);
for (int i = colNumberStart; i < colNumberStart + colSpan; i ++) {
// Add the column widths
toReturn.add(colWidthSpecs.get(i));
}
}
return toReturn;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#getTableWidth(java.lang.String)
*/
public WidthRepresentation getTableWidth(String tableCellsTagName) {
WidthRepresentation toReturn = null;
if (tableElement != null && "td".equals(tableCellsTagName)) {
AttrValue widthAttr = tableElement.getAttribute("width");
if (widthAttr != null) {
String width = widthAttr.getValue();
if (width != null) {
toReturn = new WidthRepresentation(width, true);
}
}
}
return toReturn;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#init(ro.sync.ecss.extensions.api.node.AuthorElement)
*/
public void init(AuthorElement tableElement) {
this.tableElement = tableElement;
AuthorElement[] colChildren = tableElement.getElementsByLocalName("customcol");
if (colChildren != null && colChildren.length > 0) {
for (int i = 0; i < colChildren.length; i++) {
AuthorElement colChild = colChildren[i];
if (i == 0) {
colsStartOffset = colChild.getStartOffset();
}
if (i == colChildren.length - 1) {
colsEndOffset = colChild.getEndOffset();
}
// Determine the 'width' for this col.
AttrValue colWidthAttribute = colChild.getAttribute("width");
String colWidth = null;
if (colWidthAttribute != null) {
colWidth = colWidthAttribute.getValue();
// Add WidthRepresentation objects for the columns this 'customcol' specification
// spans over.
colWidthSpecs.add(new WidthRepresentation(colWidth, true));
}
}
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isAcceptingFixedColumnWidths(java.lang.String)
*/
public boolean isAcceptingFixedColumnWidths(String tableCellsTagName) {
return "td".equals(tableCellsTagName);
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isAcceptingPercentageColumnWidths(java.lang.String)
*/
public boolean isAcceptingPercentageColumnWidths(String tableCellsTagName) {
return "td".equals(tableCellsTagName);
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isAcceptingProportionalColumnWidths(java.lang.String)
*/
public boolean isAcceptingProportionalColumnWidths(String tableCellsTagName) {
return "td".equals(tableCellsTagName);
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isTableAcceptingWidth(java.lang.String)
*/
public boolean isTableAcceptingWidth(String tableCellsTagName) {
return "td".equals(tableCellsTagName);
}
/**
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isTableAndColumnsResizable(java.lang.String)
*/
public boolean isTableAndColumnsResizable(String tableCellsTagName) {
return "td".equals(tableCellsTagName);
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Implementation for the Simple Documentation Framework table layout.";
}
}

View File

@ -0,0 +1,104 @@
package simple.documentation.framework.filters;
import javax.swing.text.BadLocationException;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorDocumentFilter;
import ro.sync.ecss.extensions.api.AuthorDocumentFilterBypass;
import ro.sync.ecss.extensions.api.AuthorOperationException;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
/**
* Simple Documentation Framework document filter used to restrict insertion and deletion of
* different nodes from the document when the strict mode is activated.
*
*/
public class SDFDocumentFilter extends AuthorDocumentFilter {
/**
* The author access.
*/
private final AuthorAccess authorAccess;
/**
* Constructor.
*
* @param access The author access.
*/
public SDFDocumentFilter(AuthorAccess access) {
this.authorAccess = access;
}
/**
* Check if the strict mode is activated
* @return <code>True</code> if the strict mode is activated.
*/
private boolean isStrictModeActivated() {
String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
return "true".equals(strictMode);
}
/**
* Insert node filter.
*/
@Override
public boolean insertNode(AuthorDocumentFilterBypass filterBypass,
int caretOffset, AuthorNode element) {
// Restrict the insertion of the "title" element if the parent element already contains a
// title element.
if (isStrictModeActivated()) {
String restrict = "title";
if(element instanceof AuthorElement) {
String elementName = ((AuthorElement) element).getLocalName();
if (restrict.equals(elementName)) {
try {
AuthorNode nodeAtOffset = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
if (nodeAtOffset != null && nodeAtOffset instanceof AuthorElement) {
AuthorElement[] elements = ((AuthorElement) nodeAtOffset).getElementsByLocalName(restrict);
if (elements != null && elements.length > 0) {
AuthorElement titleChild = elements[0];
if (titleChild != null) {
authorAccess.getWorkspaceAccess().showInformationMessage("Title already added.");
return false;
}
}
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
}
return super.insertNode(filterBypass, caretOffset, element);
}
/**
* Insert text filter.
*/
@Override
public void insertText(AuthorDocumentFilterBypass filterBypass, int caretOffset,
String toInsert) {
super.insertText(filterBypass, caretOffset, toInsert);
// If the strict mode is activated and the element where the text is inserted is the "content"
// element then surround the inserted text into a "para" element.
if (isStrictModeActivated()) {
try {
AuthorNode nodeAtOffset = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
if (nodeAtOffset != null && nodeAtOffset instanceof AuthorElement) {
if ("content".equals(((AuthorElement)nodeAtOffset).getLocalName())) {
try {
filterBypass.surroundInFragment("<para/>", caretOffset, caretOffset + toInsert.length() - 1);
authorAccess.getEditorAccess().setCaretPosition(caretOffset + toInsert.length() + 1);
} catch (AuthorOperationException e) {
e.printStackTrace();
}
}
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,76 @@
package simple.documentation.framework.listeners;
import javax.swing.text.BadLocationException;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorCaretEvent;
import ro.sync.ecss.extensions.api.AuthorCaretListener;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.exml.view.graphics.Rectangle;
import ro.sync.exml.workspace.api.Platform;
import simple.documentation.framework.ui.SDFPopupWindow;
/**
* Author caret listener used to display the XML fragment corresponding to a para element
* found at the caret position.
*
*/
public class SDFAuthorCaretListener implements AuthorCaretListener {
/**
* Access to the author specific functions.
*/
private AuthorAccess authorAcess;
/**
* The popup used to display the XML fragment.
*/
private SDFPopupWindow popupWindow;
/**
* Constructor.
*
* @param authorAccess Access to the author specific functions.
*/
public SDFAuthorCaretListener(AuthorAccess authorAccess) {
this.authorAcess = authorAccess;
// Use the information popup only if this is the standalone Oxygen version.
if (authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
// Create the information popup window.
popupWindow = new SDFPopupWindow(authorAccess, "XML Fragment:");
}
}
/**
* Caret moved.
* Display the XML fragment corresponding to a para element found at the caret
* position in a popup window.
*/
public void caretMoved(AuthorCaretEvent caretEvent) {
// Verify if the node corresponding to the new caret position corresponds to a "para" element.
if (authorAcess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
int caretOffset = authorAcess.getEditorAccess().getCaretOffset();
try {
AuthorNode nodeAtOffset = authorAcess.getDocumentController().getNodeAtOffset(caretOffset);
if (nodeAtOffset != null && nodeAtOffset instanceof AuthorElement) {
AuthorElement element = (AuthorElement) nodeAtOffset;
if ("para".equals(element.getLocalName())) {
AuthorDocumentFragment paraFragment = authorAcess.getDocumentController().createDocumentFragment(element, true);
String serializeFragmentToXML = authorAcess.getDocumentController().serializeFragmentToXML(paraFragment);
// Find the x and y coordinates from the caret shape (the popup window location).
Rectangle modelToView = authorAcess.getEditorAccess().modelToViewRectangle(authorAcess.getEditorAccess().getCaretOffset());
popupWindow.setTimeToDisplay(3);
popupWindow.display(
serializeFragmentToXML,
modelToView.x + modelToView.width,
modelToView.y + modelToView.height,
10);
}
}
} catch (BadLocationException e) {
}
}
}
}

View File

@ -0,0 +1,130 @@
package simple.documentation.framework.listeners;
import java.util.List;
import ro.sync.ecss.extensions.api.AttributeChangedEvent;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorListenerAdapter;
import ro.sync.ecss.extensions.api.DocumentContentDeletedEvent;
import ro.sync.ecss.extensions.api.DocumentContentInsertedEvent;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
/**
* Simple Documentation Framework Author listener.
*/
public class SDFAuthorListener extends AuthorListenerAdapter {
/**
* Access to the author specific functions.
*/
private AuthorAccess authorAccess;
/**
* Constructor.
*
* @param authorAccess Access to the author specific functions
*/
public SDFAuthorListener(AuthorAccess authorAccess) {
this.authorAccess = authorAccess;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#attributeChanged(ro.sync.ecss.extensions.api.AttributeChangedEvent)
*/
@Override
public void attributeChanged(AttributeChangedEvent e) {
String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
if ("true".equals(strictMode)) {
// If the changed attribute is the "column_span" or "row_span" attribute of the "td"
// element then verify if the new value is an integer value
AuthorNode ownerAuthorNode = e.getOwnerAuthorNode();
if (ownerAuthorNode instanceof AuthorElement) {
AuthorElement ownerAuthorElement = (AuthorElement) ownerAuthorNode;
if ("td".equals(ownerAuthorElement.getLocalName())) {
String attributeName = e.getAttributeName();
if ("column_span".equals(attributeName) || "row_span".equals(attributeName)) {
String spanValue = ownerAuthorElement.getAttribute(attributeName).getValue();
try {
Integer.parseInt(spanValue);
} catch (NumberFormatException ex) {
authorAccess.getWorkspaceAccess().showInformationMessage("The value " + spanValue + " of attribute " + attributeName +
" is not valid.");
}
}
}
}
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#beforeContentDelete(ro.sync.ecss.extensions.api.DocumentContentDeletedEvent)
*/
@Override
public void beforeContentDelete(DocumentContentDeletedEvent deleteEvent) {
String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
if ("true".equals(strictMode)) {
// If the section title is deleted an error message will inform the user that the title
// is required.
if (deleteEvent.getType() != DocumentContentDeletedEvent.INSERT_TEXT_EVENT
&& deleteEvent.getType() != DocumentContentDeletedEvent.DELETE_TEXT_EVENT) {
AuthorNode changedNode = deleteEvent.getParentNode();
if (changedNode instanceof AuthorElement) {
AuthorElement changedElement = (AuthorElement) changedNode;
// Section element
if ("section".equals(changedElement.getLocalName())) {
AuthorElement[] titleElements = changedElement.getElementsByLocalName("title");
// If the section has one "title" child element
if (titleElements.length == 1) {
// Find if the deleted element is the "title" one.
AuthorDocumentFragment deletedFragment = deleteEvent.getDeletedFragment();
List<AuthorNode> contentNodes = deletedFragment.getContentNodes();
for (AuthorNode authorNode : contentNodes) {
if (authorNode instanceof AuthorElement) {
if ("title".equals(((AuthorElement)authorNode).getLocalName())) {
String errorMessage = "The section must have a title.";
authorAccess.getWorkspaceAccess().showErrorMessage(errorMessage);
}
}
}
}
}
}
}
}
super.beforeContentDelete(deleteEvent);
}
/**
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#contentInserted(ro.sync.ecss.extensions.api.DocumentContentInsertedEvent)
*/
@Override
public void contentInserted(DocumentContentInsertedEvent e) {
AuthorNode node = e.getParentNode();
AuthorNode parentNode = node.getParent();
// For 'section' nodes the title text is rendered in the Outline
// (see customizeRenderingInformation method from SDFAuthorOutlineCustomizer)
// so we need to refresh the section node from the Outline when the title
// text has changed.
if ("title".equals(node.getName()) && "section".equals(parentNode.getName())) {
authorAccess.getOutlineAccess().refreshNodes(new AuthorNode[] {parentNode});
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#contentDeleted(ro.sync.ecss.extensions.api.DocumentContentDeletedEvent)
*/
@Override
public void contentDeleted(DocumentContentDeletedEvent e) {
AuthorNode node = e.getParentNode();
AuthorNode parentNode = node.getParent();
// For 'section' nodes the title text is rendered in the Outline
// (see customizeRenderingInformation method from SDFAuthorOutlineCustomizer)
// so we need to refresh the section node from the Outline when the title
// text has changed.
if ("title".equals(node.getName()) && "section".equals(parentNode.getName())) {
authorAccess.getOutlineAccess().refreshNodes(new AuthorNode[] {parentNode});
}
}
}

View File

@ -0,0 +1,62 @@
package simple.documentation.framework.listeners;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.AuthorMouseEvent;
import ro.sync.ecss.extensions.api.AuthorMouseListener;
import ro.sync.exml.workspace.api.Platform;
import simple.documentation.framework.ui.SDFPopupWindow;
/**
* Custom author mouse listener used to display the mouse coordinates in a popup window
* on mouse clicked.
*
*/
public class SDFAuthorMouseListener implements AuthorMouseListener {
/**
* Access to the author specific functions.
*/
private AuthorAccess authorAccess;
/**
* The popup used to display the mouse coordinates.
*/
private SDFPopupWindow popupWindow;
/**
* Constructor.
*
* @param authorAccess Access to the author specific functions
*/
public SDFAuthorMouseListener(AuthorAccess authorAccess) {
this.authorAccess = authorAccess;
// Use the information popup only if this is the standalone Oxygen version.
if (authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
popupWindow = new SDFPopupWindow(authorAccess, "Position");
}
}
public void mouseClicked(AuthorMouseEvent e) {
// Display the mouse coordinates.
if (authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
if (e.clickCount == 2) {
String toDisplay = "X: " + e.X + " Y: " + e.Y;
popupWindow.setTimeToDisplay(2);
popupWindow.display(toDisplay, e.X, e.Y, 10);
}
}
}
public void mousePressed(AuthorMouseEvent e) {
}
public void mouseReleased(AuthorMouseEvent e) {
}
public void mouseDragged(AuthorMouseEvent e) {
}
public void mouseMoved(AuthorMouseEvent e) {
}
}

View File

@ -0,0 +1,76 @@
package simple.documentation.framework.operations;
import java.awt.Component;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import javax.swing.JFileChooser;
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;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.basic.util.URLUtil;
/**
* Operation to save the Author node at caret in a separate document and refresh the new file path in the project.
*/
public class ExtractNodeToFileOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args) throws IllegalArgumentException,
AuthorOperationException {
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
try {
// Get node at caret
AuthorNode nodeAtCaret = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
JFileChooser fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(false);
// Show Save Dialog
if (fileChooser.showSaveDialog((Component) authorAccess.getWorkspaceAccess().getParentFrame())
== JFileChooser.APPROVE_OPTION) {
File outputFile = fileChooser.getSelectedFile();
FileOutputStream fos = new FileOutputStream(outputFile);
OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
// Write the node fragment
AuthorDocumentFragment fragment = authorAccess.getDocumentController().createDocumentFragment(nodeAtCaret, true);
String xmlFragment = authorAccess.getDocumentController().serializeFragmentToXML(fragment);
writer.write(xmlFragment);
writer.close();
// Open file
URL outputFileUrl = URLUtil.correct(outputFile);
authorAccess.getWorkspaceAccess().open(outputFileUrl);
// Refresh in project
authorAccess.getWorkspaceAccess().refreshInProject(outputFileUrl);
}
} catch (Exception e) {
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Save the Author node at caret in a separate document and refresh the new file path in the project";
}
}

View File

@ -0,0 +1,127 @@
package simple.documentation.framework.operations;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.BadLocationException;
import ro.sync.contentcompletion.xml.CIElement;
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
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;
import ro.sync.ecss.extensions.api.AuthorSchemaManager;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.exml.view.graphics.Point;
import ro.sync.exml.view.graphics.Rectangle;
/**
* Show a popup menu that contains the name of all elements that can be inserted at the caret offset.
*/
public class InsertElementOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
@SuppressWarnings("serial")
public void doOperation(final AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
try {
//Get the caret offset
final int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
//The schema manager
final AuthorSchemaManager schemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
//The context of elements.
WhatElementsCanGoHereContext ctxt = schemaManager.createWhatElementsCanGoHereContext(caretOffset);
//Get the list of elements which can be inserted here
final List<CIElement> childrenElements = schemaManager.getChildrenElements(ctxt);
JPopupMenu jpo = new JPopupMenu();
for (int i = 0; i < childrenElements.size(); i++) {
final int index = i;
jpo.add(new JMenuItem(new AbstractAction(childrenElements.get(index).getName()) {
public void actionPerformed(ActionEvent e) {
CIElement toInsert = childrenElements.get(index);
try {
//The CIElement contains all data necessary to make a small XML fragment with
//the string to insert and then call
// authorAccess.getDocumentController().createNewDocumentFragmentInContext(xmlFragment, caretOffset);
//But Oxygen 11.2 will come with a easier method:
//Create a document fragment from the CIElement.
AuthorDocumentFragment frag = schemaManager.createAuthorDocumentFragment(toInsert);
//Now you can process the fragment and remove/add attributes.
authorAccess.getDocumentController().insertFragment(caretOffset, frag);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
}));
}
Rectangle mtv = authorAccess.getEditorAccess().modelToViewRectangle(caretOffset);
Point popupLocation = authorAccess.getEditorAccess().getLocationOnScreenAsPoint(mtv.x, mtv.y);
jpo.setLocation(popupLocation.x, popupLocation.y);
JFrame oxygenFrame = (JFrame)authorAccess.getWorkspaceAccess().getParentFrame();
// Get the author component
JPanel authorComponent = (JPanel)authorAccess.getEditorAccess().getAuthorComponent();
// Get the glass pane
final Component glassPane = authorComponent.getRootPane().getGlassPane();
if (glassPane != null) {
glassPane.setVisible(true);
// Set wait cursor
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
// Show popup menu
jpo.show(oxygenFrame, popupLocation.x - oxygenFrame.getLocation().x, popupLocation.y - oxygenFrame.getLocation().y);
// Add a popup menu listener
jpo.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// Reset cursor to default
if (glassPane != null) {
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
glassPane.setVisible(false);
}
}
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
} catch (BadLocationException e) {
e.printStackTrace();
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Insert element at the caret position.";
}
}

View File

@ -0,0 +1,213 @@
package simple.documentation.framework.operations;
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;
/**
* Insert image operation.
*/
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.getWorkspaceAccess().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.getEditorAccess().getCaretOffset();
authorAccess.getDocumentController().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() {
@Override
public String getDescription() {
return "Image files";
}
@Override
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.toURI().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() {
@Override
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()));
}
}

View File

@ -0,0 +1,39 @@
package simple.documentation.framework.operations;
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;
/**
* Open the selected text in a new TXT editor.
*/
public class OpenInNewEditor implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap arg1) throws IllegalArgumentException,
AuthorOperationException {
String selectedText = authorAccess.getEditorAccess().getSelectedText();
// Open in new editor
authorAccess.getWorkspaceAccess().createNewEditor("txt", null, selectedText);
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
// No arguments
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Open new editor";
}
}

View File

@ -0,0 +1,196 @@
package simple.documentation.framework.operations;
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;
/**
* Query DB operation.
*/
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.getEditorAccess().getCaretOffset();
try {
if (jdbcDriver == null) {
throw new AuthorOperationException("No jdbc driver provided.");
}
if (connection == null) {
throw new AuthorOperationException("No connection provided.");
}
if (user == null) {
throw new AuthorOperationException("No user provided.");
}
if (sql == null) {
throw new AuthorOperationException("No sql provided.");
}
authorAccess.getDocumentController().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("&", "&amp;").
replaceAll("<", "&lt;");
}
}

View File

@ -0,0 +1,38 @@
package simple.documentation.framework.operations;
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;
import ro.sync.ecss.extensions.api.access.AuthorEditorAccess;
/**
* Refresh CSS operation.
*
*/
public class SDFRefreshCSSOperation implements AuthorOperation {
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
AuthorEditorAccess access = authorAccess.getEditorAccess();
// Reload the CSS files and perform a refresh on the whole document to recompute
// the layout
access.refresh();
}
/**
* Arguments.
*/
public ArgumentDescriptor[] getArguments() {
// No arguments
return null;
}
/**
* Description.
*/
public String getDescription() {
return "Refresh CSS operation for Simple Documentation Framework";
}
}

View File

@ -0,0 +1,45 @@
package simple.documentation.framework.operations;
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;
/**
* Show file status operation.
*
*/
public class SDFShowFileStatusOperation implements AuthorOperation {
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
// Build the file status message.
StringBuilder message = new StringBuilder();
// Editor location
message.append("Location: " + authorAccess.getEditorAccess().getEditorLocation() + "\n");
// Determine if the document from the editor contains unsaved modifications.
message.append("Modified: " + authorAccess.getEditorAccess().isModified() + "\n");
// Determine if the document from the editor was ever saved.
message.append("Untitled: " + authorAccess.getEditorAccess().isNewDocument());
// Show the informations about the file status
authorAccess.getWorkspaceAccess().showInformationMessage(message.toString());
}
/**
* Arguments.
*/
public ArgumentDescriptor[] getArguments() {
// No arguments
return null;
}
/**
* Description
*/
public String getDescription() {
return "Show the status of the current file";
}
}

View File

@ -0,0 +1,54 @@
package simple.documentation.framework.operations;
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;
/**
*
* Strict Mode Operation used to change the permissions to change parts of the document content.
*
*/
public class SDFStrictModeOperation implements AuthorOperation {
// The strict mode key used to store the strict mode option.
private String strictModeOptionKey = "strictMode";
/**
* The Strict mode has changed.
*/
public void doOperation(final AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
// Get the strict mode option value from the option storage.
String strictMode = authorAccess.getOptionsStorage().getOption(strictModeOptionKey, "false");
boolean enabled = Boolean.parseBoolean(strictMode);
// Change the strict mode option state
enabled = !enabled;
// Save the new value of the strict mode option
authorAccess.getOptionsStorage().setOption(strictModeOptionKey, String.valueOf(enabled));
// Show the strict mode operation status.
String statusMessage = "Strict Mode: " + (enabled ? " ON " : "OFF");
authorAccess.getWorkspaceAccess().showStatusMessage(statusMessage);
}
/**
* Arguments.
*/
public ArgumentDescriptor[] getArguments() {
// No arguments
return null;
}
/**
* Description
*/
public String getDescription() {
return "Strict mode operation";
}
}

View File

@ -0,0 +1,103 @@
package simple.documentation.framework.operations;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.tree.TreePath;
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;
import ro.sync.ecss.extensions.api.node.AuthorDocument;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.ecss.extensions.api.structure.AuthorOutlineCustomizer;
import simple.documentation.framework.extensions.SDFAuthorOutlineCustomizer;
/**
* Select in the Outline the children nodes of the node at caret.
*/
public class SelectNodesInOutlinerOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
// Renderer customizer used to find if a given node is filtered in the Outline.
AuthorOutlineCustomizer rendererCustomizer = new SDFAuthorOutlineCustomizer();
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
try {
// Node at caret position
AuthorNode currentNode = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
if (currentNode instanceof AuthorElement) {
AuthorElement currentElement = (AuthorElement)currentNode;
// Find the content nodes
List<AuthorNode> contentNodes = currentElement.getContentNodes();
List<TreePath> selectTreePaths = new ArrayList<TreePath>();
LinkedList<AuthorNode> reversedPath = findReversedPath(authorAccess, rendererCustomizer,
currentNode);
for (AuthorNode authorNode : contentNodes) {
if (!rendererCustomizer.ignoreNode(authorNode)) {
LinkedList<AuthorNode> pathList = new LinkedList<AuthorNode>(reversedPath);
pathList.add(authorNode);
// Add the children tree path in the selected tree paths list
selectTreePaths.add(new TreePath(pathList.toArray(new AuthorNode[0])));
}
}
// Select the children tree paths in the Outline
authorAccess.getOutlineAccess().setSelectionPaths(selectTreePaths.toArray(new TreePath[0]));
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Select nodes in the Outliner";
}
/**
* Builds the list of node parents up to and including the root node, where the
* original node is the last element in the returned array.
*
* @param authorAccess The author access.
* @param rendererCustomizer Renderer customizer used to find if a given node
* is filtered in the Outline.
* @param node The node to reverse path for.
* @return The path nodes list.
*/
private LinkedList<AuthorNode> findReversedPath(AuthorAccess authorAccess,
AuthorOutlineCustomizer rendererCustomizer, AuthorNode node) {
AuthorDocument document = authorAccess.getDocumentController().getAuthorDocumentNode();
// Builds the path.
LinkedList<AuthorNode> reversedPath = new LinkedList<AuthorNode>();
while (node != null && !rendererCustomizer.ignoreNode(node)) {
reversedPath.addFirst(node);
if (node == document) {
// Just added root.
break;
}
node = node.getParent();
}
return reversedPath;
}
}

View File

@ -0,0 +1,82 @@
package simple.documentation.framework.operations;
import java.io.File;
import java.io.StringWriter;
import java.net.MalformedURLException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.InputSource;
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;
import ro.sync.ecss.extensions.api.access.AuthorXMLUtilAccess;
/**
* Custom transform operation using Saxon EE transformer.
*/
public class TrasformOperation implements AuthorOperation {
public void doOperation(AuthorAccess authorAccess, ArgumentsMap arguments)
throws IllegalArgumentException, AuthorOperationException {
// Choose the XSLT used in the transform operation
File xsltFile = authorAccess.getWorkspaceAccess().chooseFile(
// Title
"Choose the XSLT",
// Extensions
new String[] {"xsl", "xslt"},
// Filter description
"XSLT files",
// Open for save
false);
if (xsltFile != null) {
try {
String xslSystemId = xsltFile.toURI().toURL().toString();
// Create the XSLT source used in transform operation
Source xslSource = new SAXSource(new InputSource(xslSystemId));
// Create a XSL transformer without Oxygen options
Transformer xslTransformer = authorAccess.getXMLUtilAccess().createXSLTTransformer(xslSource, null, AuthorXMLUtilAccess.TRANSFORMER_SAXON_ENTERPRISE_EDITION, false);
Source editorSource = new SAXSource(new InputSource(authorAccess.getEditorAccess().createContentReader()));
StringWriter result = new StringWriter();
// Transform the current document using the specified XSLT
xslTransformer.transform(editorSource, new StreamResult(result));
StringBuffer resultBuffer = result.getBuffer();
// Display the result
authorAccess.getWorkspaceAccess().showInformationMessage("Transformation result: " + resultBuffer.toString());
authorAccess.getWorkspaceAccess().showStatusMessage("Transformation finished");
} catch (TransformerConfigurationException e) {
// Display the error message
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
} catch (TransformerException e) {
// Display the error message
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
} catch (MalformedURLException e) {
// Display the error message
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
}
}
}
/**
* Arguments
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* Description
*/
public String getDescription() {
return "Custom transform operation using Saxon EE transformer.";
}
}

View File

@ -0,0 +1,71 @@
package simple.documentation.framework.operations.highlight;
import java.util.LinkedHashMap;
import javax.swing.JFrame;
import javax.swing.text.BadLocationException;
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;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
/**
* Operation used to highlight element from the caret position.
*/
public class AddHighlightOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap map) throws IllegalArgumentException,
AuthorOperationException {
// Show dialog for adding highlight comment
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
EditHighlightsDialog commentDlg = new EditHighlightsDialog(
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
"Add highlight comment",
true,
null,
authorAccess);
commentDlg.showDialog();
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
try {
// Highlight the node at caret
AuthorNode nodeAtCaret = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
String authorName = authorAccess.getReviewController().getReviewerAuthorName();
String timestamp = authorAccess.getReviewController().getCurrentTimestamp();
// Compose highlight properties
LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
properties.put(HighlightProperties.ID, timestamp);
properties.put(HighlightProperties.AUTHOR, authorName);
properties.put(HighlightProperties.COMMENT, commentDlg.getComment());
// Add highlight
highlighter.addHighlight(nodeAtCaret.getStartOffset(), nodeAtCaret.getEndOffset(), properties);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Highlight element from the caret position.";
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
}

View File

@ -0,0 +1,55 @@
package simple.documentation.framework.operations.highlight;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
/**
* Dialog used for changing the current review author
*/
@SuppressWarnings("serial")
public class ChangeReviewAuthorDialog extends OKCancelDialog {
/**
* Combo box containing all possible author names
*/
private JComboBox<String> authorNamesComboBox;
/**
* Constructor.
*
* @param parentFrame The parent frame.
* @param title The dialog title.
* @param authorNames All the possible author names.
*/
public ChangeReviewAuthorDialog(
JFrame parentFrame,
String title,
String[] authorNames) {
super(parentFrame, title, true);
// Add label
add(new JLabel("Choose Review Author: "));
// Add the combobox containing possible author names
authorNamesComboBox = new JComboBox<String>(authorNames);
add(authorNamesComboBox);
}
/**
* Show the dialog.
*/
public void showDialog() {
setLocationRelativeTo(null);
pack();
setVisible(true);
}
/**
* Get the selected author name.
*
* @return The selected author name.
*/
public String getSelectedAuthorName() {
return (String) authorNamesComboBox.getSelectedItem();
}
}

View File

@ -0,0 +1,51 @@
package simple.documentation.framework.operations.highlight;
import javax.swing.JFrame;
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;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
/**
* Operation that allow changing the author.
*/
public class ChangeReviewAuthorOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
ChangeReviewAuthorDialog commentDlg = new ChangeReviewAuthorDialog(
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
"Change Review Author",
new String[] {"Author_1", "Author_2", "Author_3", "Default"});
// Show the dialog
commentDlg.showDialog();
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
String reviewerAuthorName = commentDlg.getSelectedAuthorName();
// If the the reviewer author name is set to null, the default author name is used.
reviewerAuthorName = "Default".equals(reviewerAuthorName) ? null : reviewerAuthorName;
// Set the reviewer author name
authorAccess.getReviewController().setReviewerAuthorName(reviewerAuthorName);
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Change review author name";
}
}

View File

@ -0,0 +1,190 @@
package simple.documentation.framework.operations.highlight;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.BevelBorder;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
/**
* Dialog used for adding or editing highlights.
*/
@SuppressWarnings("serial")
public class EditHighlightsDialog extends OKCancelDialog {
/**
* Information label (presents highlight ID and author).
*/
private JLabel infoLabel;
/**
* Comment area.
*/
private JTextArea commentArea;
/**
* Map between highlight and edited properties.
*/
private Map<AuthorPersistentHighlight, LinkedHashMap<String, String>> mapHighlightsToProps;
/**
* Current edited highlight index.
*/
private final int[] currentIndex = new int[1];
/**
* Current edited highlight.
*/
private AuthorPersistentHighlight currentHighlight;
/**
* The Author access.
*/
private final AuthorAccess authorAccess;
/**
* Constructor.
*
* @param parentFrame The parent frame.
* @param title The dialog title.
* @param modal <code>true</code> if modal.
* @param highlights List of highlights to be edited.
* @param auhorAccess The Author access
*/
public EditHighlightsDialog(
JFrame parentFrame,
String title,
boolean modal,
final List<AuthorPersistentHighlight> highlights,
final AuthorAccess auhorAccess) {
super(parentFrame, title, modal);
this.authorAccess = auhorAccess;
boolean editHighlights = highlights != null && highlights.size() > 0;
commentArea = new JTextArea();
commentArea.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
// Add scroll pane
JScrollPane commentAreaPane = new JScrollPane(commentArea);
commentAreaPane.setPreferredSize(new Dimension(400, 200));
commentArea.setMargin(new Insets(10, 10, 10 , 10));
// Add comment area
add(commentAreaPane, BorderLayout.CENTER);
if (editHighlights) {
mapHighlightsToProps = new LinkedHashMap<AuthorPersistentHighlight, LinkedHashMap<String,String>>();
currentHighlight = highlights.get(0);
JPanel northPanel = new JPanel();
infoLabel = new JLabel();
northPanel.add(infoLabel);
// Add next button if necessary
if (highlights.size() > 1) {
final JButton nextButton = new JButton("Next");
nextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
saveCurrentHighlightProps(currentHighlight);
currentIndex[0] = currentIndex[0] + 1;
currentHighlight = highlights.get(currentIndex[0]);
// Display next highlight
displayHighlightProps(currentHighlight);
// Disable next button if necessary
if (highlights.size() == currentIndex[0] + 1) {
nextButton.setEnabled(false);
}
}
});
// Add next button
northPanel.add(nextButton);
}
add(northPanel, BorderLayout.NORTH);
// Display properties for the current highlight
displayHighlightProps(currentHighlight);
}
}
private void saveCurrentHighlightProps(AuthorPersistentHighlight highlight) {
// Save edited properties for the current map
LinkedHashMap<String, String> props = highlight.getClonedProperties();
props.put(HighlightProperties.COMMENT, commentArea.getText());
mapHighlightsToProps.put(highlight, props);
}
/**
* @see ro.sync.ecss.extensions.commons.ui.OKCancelDialog#doOK()
*/
@Override
protected void doOK() {
if (currentHighlight != null) {
saveCurrentHighlightProps(currentHighlight);
if (mapHighlightsToProps != null) {
Set<AuthorPersistentHighlight> highlights = mapHighlightsToProps.keySet();
for (AuthorPersistentHighlight highlight : highlights) {
// Update the timestamp
mapHighlightsToProps.get(highlight).put(HighlightProperties.ID, authorAccess.getReviewController().getCurrentTimestamp());
}
}
}
super.doOK();
}
/**
* Display highlight properties.
*
* @param highlight The current highlight.
*/
private void displayHighlightProps(AuthorPersistentHighlight highlight) {
LinkedHashMap<String, String> cloneProperties = highlight.getClonedProperties();
// Highlight properties
String id = cloneProperties.get(HighlightProperties.ID);
String author = cloneProperties.get(HighlightProperties.AUTHOR);
String comment = cloneProperties.get(HighlightProperties.COMMENT);
// Display highlight comment
commentArea.setText(comment);
// Display highlight ID and author
infoLabel.setText("Id: " + id + " Author: " + author);
}
/**
* Show the dialog.
*/
public void showDialog() {
setLocationRelativeTo(null);
pack();
setVisible(true);
}
/**
* Get the last edited comment.
*
* @return The inserted comment.
*/
public String getComment() {
return commentArea.getText();
}
/**
* @return Returns the highlights to properties map.
*/
public Map<AuthorPersistentHighlight, LinkedHashMap<String, String>> getMapHighlightsToProps() {
return mapHighlightsToProps;
}
}

View File

@ -0,0 +1,79 @@
package simple.documentation.framework.operations.highlight;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JFrame;
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;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
/**
* Operation used to edit the highlights from the caret position.
*/
public class EditHighlightsOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
AuthorPersistentHighlight[] highlights = highlighter.getHighlights();
if (highlights.length > 0) {
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
List<AuthorPersistentHighlight> caretHighlights = new ArrayList<AuthorPersistentHighlight>();
// Remove highlights from the caret position
for (AuthorPersistentHighlight highlight : highlights) {
// Get the highlights from the caret position
if (highlight.getStartOffset() <= caretOffset && highlight.getEndOffset() >= caretOffset) {
caretHighlights.add(highlight);
}
}
if (caretHighlights.size() > 0) {
// Show edit highlights dialog
EditHighlightsDialog commentDlg = new EditHighlightsDialog(
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
"Add highlight comment",
true,
caretHighlights,
authorAccess);
commentDlg.showDialog();
// Save edited highlights if dialog result is OK
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
Map<AuthorPersistentHighlight, LinkedHashMap<String, String>> mapHighlightsToProps = commentDlg.getMapHighlightsToProps();
Set<AuthorPersistentHighlight> highlightsSet = mapHighlightsToProps.keySet();
for (AuthorPersistentHighlight h : highlightsSet) {
// Save edited properties
highlighter.setProperties(h, mapHighlightsToProps.get(h));
}
}
}
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Edit Highlights from the caret position";
}
}

View File

@ -0,0 +1,19 @@
package simple.documentation.framework.operations.highlight;
/**
* Hightlight properties names
*/
public interface HighlightProperties {
/**
* Highlight author.
*/
static final String AUTHOR = "author";
/**
* Highlight ID.
*/
static final String ID = "id";
/**
* Highlight comment.
*/
static final String COMMENT = "comment";
}

View File

@ -0,0 +1,41 @@
package simple.documentation.framework.operations.highlight;
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;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
/**
* Operation used to remove all persistent highlights.
*/
public class RemoveAllHighlightsOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
int highlights = highlighter.getHighlights().length;
// Remove all highlights
highlighter.removeAllHighlights();
authorAccess.getWorkspaceAccess().showInformationMessage(highlights == 1 ? "1 highlight removed" : highlights + " highlights removed");
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Remove all persistent highlights";
}
}

View File

@ -0,0 +1,108 @@
package simple.documentation.framework.operations.highlight;
import java.awt.Component;
import java.util.LinkedHashMap;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.text.BadLocationException;
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;
import ro.sync.ecss.extensions.api.highlights.AuthorHighlighter;
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
import ro.sync.ecss.extensions.api.highlights.ColorHighlightPainter;
import ro.sync.ecss.extensions.api.node.AuthorNode;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
import ro.sync.exml.view.graphics.Color;
/**
* Operation used to highlight a paragraph as reviewed.
*/
public class ReviewParaOperation implements AuthorOperation {
/**
* Highlight author.
*/
static final String AUTHOR = "author";
/**
* Highlight ID.
*/
static final String ID = "id";
/**
* Highlight comment.
*/
static final String COMMENT = "comment";
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap map) throws IllegalArgumentException,
AuthorOperationException {
// Highlight the selected paragraph if any.
AuthorNode selectedNode = authorAccess.getEditorAccess().getFullySelectedNode();
if (selectedNode != null) {
// Show dialog for adding highlight comment
AuthorPersistentHighlighter persistentHighlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
AuthorHighlighter highlighter = authorAccess.getEditorAccess().getHighlighter();
EditHighlightsDialog commentDlg = new EditHighlightsDialog(
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
"Review Paragraph",
true,
null,
authorAccess);
commentDlg.showDialog();
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
// Get author name and timestamp.
String authorName = authorAccess.getReviewController().getReviewerAuthorName();
String timestamp = authorAccess.getReviewController().getCurrentTimestamp();
// Compose highlight properties
LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
properties.put(ID, timestamp);
properties.put(AUTHOR, authorName);
String comment = commentDlg.getComment();
properties.put(COMMENT, comment);
int startOffset = selectedNode.getStartOffset();
int endOffset = selectedNode.getEndOffset();
if (comment != null && comment.trim().length() > 0) {
// Add a persistent highlight
persistentHighlighter.addHighlight(startOffset, endOffset, properties);
} else {
// Add non-persistent highlight
ColorHighlightPainter painter = new ColorHighlightPainter();
painter.setTextForegroundColor(Color.COLOR_RED_DARKER);
try {
highlighter.addHighlight(startOffset, endOffset, painter, null);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
} else {
JOptionPane.showMessageDialog(
(Component) authorAccess.getWorkspaceAccess().getParentFrame(),
"Select the whole paragraph!!!");
}
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Review selected paragraph.";
}
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
}

View File

@ -0,0 +1,89 @@
package simple.documentation.framework.operations.table;
import java.awt.Component;
import javax.swing.JFrame;
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;
import ro.sync.exml.workspace.api.Platform;
import simple.documentation.framework.operations.table.TableCustomizerDialog.TableInfo;
/**
* Operation used to insert a SDF table.
*/
public class InsertTableOperation implements AuthorOperation {
/**
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
*/
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
throws IllegalArgumentException, AuthorOperationException {
// Show the 'Insert table' dialog
TableInfo tableInfo = null;
if(authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
TableCustomizerDialog tableCustomizerDialog = new TableCustomizerDialog(
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame());
tableCustomizerDialog.setLocationRelativeTo(
(Component) authorAccess.getWorkspaceAccess().getParentFrame());
tableInfo = tableCustomizerDialog.showDialog();
}
if (tableInfo != null) {
// Create the table XML fragment
StringBuffer tableXMLFragment = new StringBuffer();
tableXMLFragment.append("<table xmlns=\"http://www.oxygenxml.com/sample/documentation\"");
if (tableInfo.getTableBackgroundColor() != null) {
tableXMLFragment.append(" bgcolor=\"rgb(" +
tableInfo.getTableBackgroundColor().getRed() + "," +
tableInfo.getTableBackgroundColor().getGreen() + "," +
tableInfo.getTableBackgroundColor().getBlue() +
")\"");
}
tableXMLFragment.append(">");
if(tableInfo.getTitle() != null && tableInfo.getTitle().trim().length() > 0) {
tableXMLFragment.append("<title>" + tableInfo.getTitle().trim() + "</title>");
}
// Add table body
int columns = tableInfo.getColumnsNumber();
int rows = tableInfo.getRowsNumber();
for (int i = 0; i < rows; i++) {
tableXMLFragment.append("<tr>");
for (int j = 0; j < columns; j++) {
tableXMLFragment.append("<td></td>");
}
tableXMLFragment.append("</tr>");
}
tableXMLFragment.append("</table>");
// Insert the table
authorAccess.getDocumentController().insertXMLFragmentSchemaAware(
tableXMLFragment.toString(),
authorAccess.getEditorAccess().getCaretOffset());
} else {
// User canceled the operation
}
}
/**
* No arguments. The operation will display a dialog for choosing the table attributes.
*
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
*/
public ArgumentDescriptor[] getArguments() {
return null;
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
public String getDescription() {
return "Insert a SDF table";
}
}

View File

@ -0,0 +1,339 @@
package simple.documentation.framework.operations.table;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import ro.sync.ecss.extensions.commons.table.operations.TableCustomizerConstants;
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
/**
* Dialog used to customize the insertion of a table (number of rows, columns, table caption).
* It is used on Standalone implementation.
*/
@SuppressWarnings("serial")
public class TableCustomizerDialog extends OKCancelDialog implements TableCustomizerConstants {
/**
* If selected the user can specify the table title.
*/
private JCheckBox titleCheckbox;
/**
* Text field for specify the table title.
*/
private JTextField titleTextField;
/**
* Used to specify the number of rows.
*/
private JSpinner rowsSpinner;
/**
* Used to specify the number of columns.
*/
private JSpinner columnsSpinner;
/**
* If selected the user can specify the table background color.
*/
private JCheckBox tableBgColorCheckbox;
/**
* Button used to choose table background color.
*/
private JButton tableBgColorButton;
/**
* Constructor.
*
* @param parentFrame The parent {@link JFrame} of the dialog.
*/
public TableCustomizerDialog(
JFrame parentFrame) {
super(parentFrame, "Insert Table", true);
// The main panel
JPanel mainPanel = new JPanel(new GridBagLayout());
mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
GridBagConstraints gridBagConstr = new GridBagConstraints();
gridBagConstr.anchor = GridBagConstraints.WEST;
gridBagConstr.gridy = 0;
gridBagConstr.gridx = 0;
gridBagConstr.weightx = 0;
gridBagConstr.gridwidth = 1;
gridBagConstr.insets = new Insets(5, 0, 5, 5);
gridBagConstr.fill = GridBagConstraints.NONE;
// Title check box
titleCheckbox = new JCheckBox("Title");
titleCheckbox.setName("Title checkbox");
titleCheckbox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
titleTextField.setEditable(titleCheckbox.isSelected());
}
});
titleCheckbox.setBorder(BorderFactory.createEmptyBorder());
mainPanel.add(titleCheckbox, gridBagConstr);
// Title text field
titleTextField = new JTextField();
titleTextField.setName("Title text field");
gridBagConstr.gridx ++;
gridBagConstr.weightx = 1;
gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
gridBagConstr.insets = new Insets(5, 0, 5, 0);
mainPanel.add(titleTextField, gridBagConstr);
gridBagConstr.gridy ++;
gridBagConstr.gridx = 0;
gridBagConstr.weightx = 0;
gridBagConstr.gridwidth = 1;
gridBagConstr.insets = new Insets(5, 0, 5, 5);
gridBagConstr.fill = GridBagConstraints.BOTH;
// Table bgcolor box
tableBgColorCheckbox = new JCheckBox("Table Background");
tableBgColorButton = new JButton();
tableBgColorCheckbox.setName("Table Background");
tableBgColorCheckbox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tableBgColorButton.setEnabled(tableBgColorCheckbox.isSelected());
}
});
tableBgColorCheckbox.setBorder(BorderFactory.createEmptyBorder());
mainPanel.add(tableBgColorCheckbox, gridBagConstr);
// Table bg color
tableBgColorButton.setIcon(new Icon() {
public void paintIcon(Component parent, Graphics g, int x, int y) {
Color color = tableBgColorButton.getBackground();
if (color == null) {
return;
}
Color used4Draw = color;
if (parent != null && !parent.isEnabled()) {
used4Draw = parent.getBackground();
}
g.setColor(used4Draw);
g.fillRect(x, y, getIconWidth(), getIconHeight());
g.setColor(used4Draw.darker());
g.drawRect(x, y, getIconWidth() - 1, getIconHeight() - 1);
}
public int getIconWidth() {
return tableBgColorButton.getWidth();
}
public int getIconHeight() {
return tableBgColorButton.getHeight();
}
});
tableBgColorButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
Color initialBackground = tableBgColorButton.getBackground();
Color background = JColorChooser.showDialog(null,
"Choose Color", initialBackground);
if (background != null) {
tableBgColorButton.setBackground(background);
tableBgColorButton.setContentAreaFilled(true);
}
}
});
tableBgColorButton.setPreferredSize(new Dimension(100, 15));
gridBagConstr.gridx ++;
gridBagConstr.weightx = 1;
gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
gridBagConstr.insets = new Insets(2, 2, 2, 2);
mainPanel.add(tableBgColorButton, gridBagConstr);
// Table size panel
JPanel sizePanel = new JPanel(new GridBagLayout());
sizePanel.setBorder(BorderFactory.createTitledBorder("Table Size"));
gridBagConstr.gridy ++;
gridBagConstr.gridx = 0;
gridBagConstr.weightx = 1;
gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
gridBagConstr.gridwidth = 2;
gridBagConstr.insets = new Insets(5, 0, 5, 0);
mainPanel.add(sizePanel, gridBagConstr);
// 'Rows' label
JLabel rowsLabel = new JLabel("Rows");
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0;
c.insets = new Insets(0, 5, 5, 5);
sizePanel.add(rowsLabel, c);
// Number of rows text field
rowsSpinner = new JSpinner();
rowsSpinner.setName("Rows spinner");
rowsSpinner.setModel(new SpinnerNumberModel(2, 0, 100, 1));
c.gridx++;
c.weightx = 1;
sizePanel.add(rowsSpinner, c);
// 'Columns' label
JLabel columnsLabel = new JLabel("Columns");
c.gridx++;
c.weightx = 0;
sizePanel.add(columnsLabel, c);
// Number of rows text field
columnsSpinner = new JSpinner();
columnsSpinner.setName("Columns spinner");
columnsSpinner.setModel(new SpinnerNumberModel(2, 0, 100, 1));
c.gridx++;
c.weightx = 1;
sizePanel.add(columnsSpinner, c);
//Add the main panel
getContentPane().add(mainPanel, BorderLayout.CENTER);
pack();
setResizable(false);
}
/**
* Contains informations about the table element.
*/
class TableInfo {
/**
* Table title.
*/
private final String title;
/**
* Table rows number.
*/
private final int rowsNumber;
/**
* Table columns number.
*/
private final int columnsNumber;
/**
* Table background color.
*/
private final Color tableBgColor;
/**
* @param title Table title.
* @param rowsNumber Table rows number.
* @param columnsNumber Table columns number.
* @param tableBgColor Table background color.
*/
public TableInfo(String title, int rowsNumber, int columnsNumber, Color tableBgColor) {
this.title = title;
this.rowsNumber = rowsNumber;
this.columnsNumber = columnsNumber;
this.tableBgColor = tableBgColor;
}
/**
* @return Returns the title.
*/
public String getTitle() {
return title;
}
/**
* @return Returns the rows number.
*/
public int getRowsNumber() {
return rowsNumber;
}
/**
* @return Returns the columns number.
*/
public int getColumnsNumber() {
return columnsNumber;
}
/**
* @return Returns the table background color.
*/
public Color getTableBackgroundColor() {
return tableBgColor;
}
}
/**
* Show the dialog to customize the table attributes.
*
* @return The object containing informations about the table to be inserted.
* If <code>null</code> then the user canceled the table insertion.
*/
public TableInfo showDialog() {
// Reset components to default values
titleTextField.setEditable(true);
titleTextField.setText("");
titleCheckbox.setSelected(true);
tableBgColorButton.setEnabled(false);
tableBgColorButton.setBackground(tableBgColorCheckbox.getBackground());
tableBgColorCheckbox.setSelected(false);
// Set the default number of rows and columns
rowsSpinner.setValue(new Integer(3));
columnsSpinner.setValue(new Integer(2));
// Request focus in title field
titleTextField.requestFocus();
super.setVisible(true);
TableInfo tableInfo = null;
if(getResult() == RESULT_OK) {
// Title
String title = null;
if(titleCheckbox.isSelected()) {
title = titleTextField.getText();
title = ro.sync.basic.xml.BasicXmlUtil.escape(title);
}
// Table background color
Color tableBgColor = null;
if(tableBgColorCheckbox.isSelected()) {
tableBgColor = tableBgColorButton.getBackground();
}
int rowsNumber = ((Integer)rowsSpinner.getValue()).intValue();
int columnsNumber = ((Integer)columnsSpinner.getValue()).intValue();
tableInfo =
new TableInfo(
title,
rowsNumber,
columnsNumber,
tableBgColor);
} else {
// Cancel was pressed
}
return tableInfo;
}
}

View File

@ -0,0 +1,110 @@
package simple.documentation.framework.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.IllegalComponentStateException;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JWindow;
import ro.sync.ecss.extensions.api.AuthorAccess;
import ro.sync.exml.view.graphics.Point;
/**
* Popup window used to display Simple Documentation Framework specific information.
*
*/
@SuppressWarnings("serial")
public class SDFPopupWindow extends JWindow {
/**
* Text area used to display useful informations.
*/
private JTextArea infoTextArea;
/**
* Access to the author specific functions.
*/
AuthorAccess authorAccess;
/**
* The display time of the popup window (seconds).
*/
private int timeToDisplay;
/**
*
* @param access Author access.
* @param infoDescription Description.
*/
public SDFPopupWindow(AuthorAccess access, String infoDescription) {
super((JFrame) access.getWorkspaceAccess().getParentFrame());
this.authorAccess = access;
// Create information text area.
infoTextArea = new JTextArea();
infoTextArea.setLineWrap(true);
infoTextArea.setWrapStyleWord(true);
infoTextArea.setEditable(false);
infoTextArea.setFocusable(false);
JPanel mainContent = new JPanel(new BorderLayout());
if (infoDescription != null) {
mainContent.add(new JLabel(infoDescription), BorderLayout.NORTH);
}
mainContent.setFocusable(false);
mainContent.add(infoTextArea, BorderLayout.SOUTH);
mainContent.setBorder(BorderFactory.createLineBorder(Color.black));
getContentPane().add(mainContent);
setVisible(false);
}
/**
* Set the time to display this popup window.
*
* @param timeToDisplay The display time in seconds.
*/
public void setTimeToDisplay(int timeToDisplay) {
this.timeToDisplay = timeToDisplay;
}
/**
* Display the specified text information.
*
* @param text The text to be displayed in the popup window.
* @param relX The "x" coordinate relative to the viewport.
* @param relY The "y" coordinate relative to the viewport.
* @param delta The translation point where the popup should be displayed from the given (x, y) point.
*/
public void display(String text, int relX, int relY, int delta) {
// Transform the given relative coordinates into absolute coordinates.
try {
Point translatedPoint = authorAccess.getEditorAccess().getLocationOnScreenAsPoint(relX, relY);
setVisible(false);
infoTextArea.setText(text);
setLocation(translatedPoint.x + delta, translatedPoint.y + delta);
pack();
// Show the information popup window
setVisible(true);
// Hide the window when the given display time is finished.
if (timeToDisplay > 0) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
setVisible(false);
}
}, timeToDisplay * 1000);
}
} catch (IllegalComponentStateException e) {
// Do nothing
}
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<article
xmlns="http://www.oxygenxml.com/sample/documentation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<title></title>
<section>
<title></title>
<para></para>
<para></para>
</section>
</article>

View File

@ -0,0 +1,22 @@
<?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>Book Template Title</title>
<section>
<title>Section Title</title>
<abs:def/>
<para>This content is copyrighted:</para>
<table>
<title>Table Title</title>
<header>
<td>Company</td>
<td>Date</td>
</header>
<tr>
<td/>
<td/>
</tr>
</table>
</section>
</book>

View File

@ -0,0 +1,34 @@
/*
* Unexpected things happen here...
*/
function a() {}
b = function() {}
// Notice the weird behavior :
typeof a; // object
typeof b; // function
aclass = function () {
this.setFoo(1);
return this;
}
typeof aclass;
aclass.prototype.setFoo = function (v) {
this.foo = v;
}
anObject = new aclass() ;
anObject.setFoo(2);
anObject.foo;
// Also worth mentionning, you ***must*** use new and this doesn't work :
//
// anobject = aclass();

View File

@ -0,0 +1,68 @@
<?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>