Hi,
Maybe I can tell you about something similar we implemented.
Let's say you open this sample file
OXYGEN_INSTALL_DIR\samples\tei\TEI-P5.xml which is a TEI document (a custom XML vocabulary used to encode old manuscripts) in the Text editing mode. And you find in it this div:
If you place the caret inside the xml:id value you will get highlighted on the scroll bar all references to it.
So how does it work? This is not a plugin customization but a framework customization:
http://blog.oxygenxml.com/2014/08/the-o ... works.html
In the Oxygen Preferences->
Document Type Association page there is a document type association called
TEI P5. It's activated when a TEI document is opened and it provides besides a default schema and CSS for visual editing also an extension JAR library in its "Classpath" tab.
In the "Extensions" tab there is a custom extensions bundle implementation set. The
TEIP5ExtensionsBundle implementation is defined in that custom JAR library:
https://www.oxygenxml.com/doc/versions/ ... undle.html
and it overrides a method in the base:
Code: Select all
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createIDTypeRecognizer()
*/
@Override
public IDTypeRecognizer createIDTypeRecognizer() {
return new TEIP5IDTypeRecognizer();
}
That
TEIP5IDTypeRecognizer is responsible for recognizing IDs and ID references.
It's Java source looks like this:
Code: Select all
/**
* Implementation of ID declarations and references recognizer for TEI P5 framework.
*
* In this framework the IDs are declared in attributes with name 'id'. The references are recognized
* in attributes ptr/@target or ref/@target, see http://www.tei-c.org/release/doc/tei-p5-doc/en/html/ref-ptr.html.
*
* @author radu_pisoi
*/
@API(type=APIType.INTERNAL, src=SourceType.PUBLIC)
public class TEIP5IDTypeRecognizer extends IDTypeRecognizer {
/**
* @see ro.sync.ecss.extensions.api.link.IDTypeRecognizer#detectIDType(java.lang.String, ro.sync.contentcompletion.xml.Context, java.lang.String, java.lang.String, java.lang.String, int)
*/
@Override
public List<IDTypeIdentifier> detectIDType(String systemID, Context context, String attrName,
String attrNs, String attributeValue, int offset) throws CannotRecognizeIDException {
List<IDTypeIdentifier> idTypeIdentifiers = new ArrayList<IDTypeIdentifier>();
if(attributeValue != null && attributeValue.trim().length() > 0) {
if("id".equals(attrName)) {
// xml:id attribute
DefaultIDTypeIdentifier idTypeIdentifier = new DefaultIDTypeIdentifier(attributeValue.trim(), true);
idTypeIdentifiers.add(idTypeIdentifier);
} else if("target".equals(attrName)) {
// 'target' attribute
Stack<ContextElement> elementStack = context.getElementStack();
if(!elementStack.isEmpty()) {
// For ptr/@target or ref/@target the ID references are recognized if the attribute value
// has the pattern #id1 #id2
String idValue = null;
StringTokenizer stringTokenizer = new StringTokenizer(attributeValue, " ", true);
int idx = 0;
while(stringTokenizer.hasMoreTokens()) {
String nextToken = stringTokenizer.nextToken();
if(offset < idx) {
break;
}
if(idx <= offset && offset <= idx + nextToken.length()) {
// Current token include the offset
if(!nextToken.equals(" ")) {
idValue = nextToken;
}
break;
}
idx += nextToken.length();
}
if (idValue != null && !"".equals(idValue.trim()) && idValue.startsWith("#")) {
idValue = idValue.substring(1);
if (idValue.trim().length() > 0) {
idTypeIdentifiers.add(new DefaultIDTypeIdentifier(idValue, false));
}
}
}
}
}
return idTypeIdentifiers.isEmpty() ? null : idTypeIdentifiers;
}
/**
* @see ro.sync.ecss.extensions.api.link.IDTypeRecognizer#locateIDType(java.lang.String, ro.sync.contentcompletion.xml.Context, java.lang.String, java.lang.String, java.lang.String, ro.sync.ecss.extensions.api.link.IDTypeIdentifier, short)
*/
@Override
public int[] locateIDType(String systemID, Context context, String attrName, String attrNs,
String attributeValue, IDTypeIdentifier idIdentifier, short mode) {
int[] idLocation = null;
if ((mode & MODE_LOCATE_DECLARATIONS) != 0) {
// xml:id declaration
if("id".equals(attrName)) {
idLocation = new int[] {0, attributeValue.length()};
}
}
if ((mode & MODE_LOCATE_REFERENCES) != 0) {
if("target".equals(attrName)) {
Stack<ContextElement> elementStack = context.getElementStack();
if(!elementStack.isEmpty()) {
String idValue = idIdentifier.getValue();
String textToFind = "#" + idValue;
int indexOf = attributeValue.indexOf(textToFind);
while (indexOf >= 0) {
if(indexOf + textToFind.length() == attributeValue.length() ||
attributeValue.charAt(indexOf + textToFind.length()) == ' ') {
idLocation = new int[] { indexOf + 1, indexOf + 1 + idIdentifier.getValue().length() };
}
if (idLocation != null) {
break;
} else {
indexOf = attributeValue.indexOf(textToFind, indexOf + textToFind.length());
}
}
}
}
}
return idLocation;
}
/**
* @see ro.sync.ecss.extensions.api.link.IDTypeRecognizer#isDefaultIDTypeRecognitionAvailable()
*/
@Override
public boolean isDefaultIDTypeRecognitionAvailable() {
return false;
}
/**
* @see ro.sync.ecss.extensions.api.link.IDTypeRecognizer#isIDTypeRecognitionAvailable()
*/
@Override
public boolean isIDTypeRecognitionAvailable() {
return true;
}
}
So if your case resembles this case you may be able to implement it at framework level using this extension.
Regards,
Radu