Unique ID from copy / paste
Post here questions and problems related to oXygen frameworks/document types.
Unique ID from copy / paste
We have many elements in our schema that have unique "id" attributes. So far we have been injecting a random id when a user creates one of these elements through our framework in Author. Now we are working on preventing duplicate id's when a user copy/paste an element in Author mode. Our initial attempt was by extending AuthorDocumentFilter and overriding the insertFragment() method. This works to intercept an AuthorDocumentFragment, determine if there is an id, check the document to see if id exists, and update the id before passing to the AuthorDocumentFilterBypass object to inject the fragment with a fresh id.
This seems like a lot of overhead, as any operation that inserts content calls this method and we can safely ignore anything without id attributes. I have been scouring the api documents for a better solution. I have come across the addUniqueAttributesProccessor() method in the AuthorDocumentController class which looks promising, but I am having difficulty understanding how it is used based on documentation. Can this be used as a way to solve my problem? Can anyone provide a use case for this? There are other classes / interfaces which seem to deal with unique attributes, but again, I can't seem to decipher how they would be used.
Thanks,
Chris
This seems like a lot of overhead, as any operation that inserts content calls this method and we can safely ignore anything without id attributes. I have been scouring the api documents for a better solution. I have come across the addUniqueAttributesProccessor() method in the AuthorDocumentController class which looks promising, but I am having difficulty understanding how it is used based on documentation. Can this be used as a way to solve my problem? Can anyone provide a use case for this? There are other classes / interfaces which seem to deal with unique attributes, but again, I can't seem to decipher how they would be used.
Thanks,
Chris
Re: Unique ID from copy / paste
Hi,
What you found works. A better API method would have been to implement this callback:
ro.sync.ecss.extensions.api.ExtensionsBundle.getClipboardFragmentProcessor()
the default implementation for it (located in the ro.sync.ecss.extensions.commons.id.DefaultUniqueAttributesRecognizer) does something like:
Regards,
Radu
What you found works. A better API method would have been to implement this callback:
ro.sync.ecss.extensions.api.ExtensionsBundle.getClipboardFragmentProcessor()
the default implementation for it (located in the ro.sync.ecss.extensions.commons.id.DefaultUniqueAttributesRecognizer) does something like:
Code: Select all
/**
* @see ro.sync.ecss.extensions.api.content.ClipboardFragmentProcessor#process(ro.sync.ecss.extensions.api.content.ClipboardFragmentInformation)
*/
@Override
public void process(ClipboardFragmentInformation fragmentInformation) {
if(authorAccess == null) {
//EXM-20948 partial fix for NPE
logger.warn("NULL Author Access, should not happen", new Exception());
return;
}
GenerateIDElementsInfo currentElemsInfo = getGenerateIDElementsInfo();
if(currentElemsInfo != null && ! currentElemsInfo.isFilterIDsOnCopy()) {
//No filtering will be done.
return;
}
//Remove unique IDs when pasting in other documents or when copy/paste in the same document
boolean removeUniqueIDs = false;
if(fragmentInformation.getFragmentOriginalLocation() != null
&& ! fragmentInformation.getFragmentOriginalLocation().equals(authorAccess.getEditorAccess().getEditorLocation().toString())) {
if(preserveIDsWhenPastingBetweenResources()){
//EXM-21408 Paste from another document. Preserve the IDs
removeUniqueIDs = false;
} else {
removeUniqueIDs = true;
}
} else {
int purposeID = fragmentInformation.getPurposeID();
if(purposeID == AuthorSchemaAwareEditingHandler.CREATE_FRAGMENT_PURPOSE_COPY
|| purposeID == AuthorSchemaAwareEditingHandler.CREATE_FRAGMENT_PURPOSE_DND_COPY) {
removeUniqueIDs = true;
}
}
if(removeUniqueIDs) {
//Remove unique IDs
filterIDAttributes(fragmentInformation.getFragment().getContentNodes());
}
}
/**
* Check if we should preserve IDs when pasting between resources.
*
* @return <code>true</code> if we should preserve IDs when pasting between resources.
* By default the base method returns <code>true</code>.
*/
protected boolean preserveIDsWhenPastingBetweenResources() {
return true;
}
/**
* Filter all ID attributes from the fragment.
*
* @param contentNodes The nodes.
*/
private void filterIDAttributes(List<AuthorNode> contentNodes) {
for (int i = 0; i < contentNodes.size(); i++) {
AuthorNode node = contentNodes.get(i);
if(node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
//Remove the ID attribute.
((AuthorElement)node).removeAttribute(idAttrQname);
}
if(node instanceof AuthorParentNode) {
filterIDAttributes(((AuthorParentNode)node).getContentNodes());
}
}
}
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
Hello everybody,
I have an issue when I try to use my own CustomDefaultUniqueAttributesRecognizer.
I have created a CustomExtensionsBundle class in which I instantiate the AttributesRecognizer :
In my framework, I put that :
So the CustomUniqueAttributesRecognizer "works" in the sense that the method process is called when I perform a paste in my document. BUT, the authorAccess is always null. I know that the CustomUniqueAttributesRecognizer implements AuthorExtensionStateListener so... how to register the CustomUniqueAttributesRecognizer in the AuthorExtensionStateListeners ?
There is a section in the framework :
But I cannot simply put :
because CustomUniqueAttributesRecognizer was instantiated by the CustomExtensionsBundle...
Thanks for your help,
Johann
I have an issue when I try to use my own CustomDefaultUniqueAttributesRecognizer.
I have created a CustomExtensionsBundle class in which I instantiate the AttributesRecognizer :
Code: Select all
public ClipboardFragmentProcessor getClipboardFragmentProcessor() {
return new CustomUniqueAttributesRecognizer();
}
Code: Select all
<field name="extensionsBundleClassName">
<String>package.CustomExtensionsBundle</String>
</field>
There is a section in the framework :
Code: Select all
<field name="authorExtensionStateListener">
Code: Select all
<field name="authorExtensionStateListener">
<String>package.CustomUniqueAttributesRecognizer</String>
</field>
Thanks for your help,
Johann
Re: Unique ID from copy / paste
Hi Johann,
Indeed the same unique attributes recognizer needs to be connected as an extension state listener. This can be done entirely from the extensions bundle implementation.
As an example here's how the DITAExtensionsBundle (used for the DITA standard) does things:
Regards,
Radu
Indeed the same unique attributes recognizer needs to be connected as an extension state listener. This can be done entirely from the extensions bundle implementation.
As an example here's how the DITAExtensionsBundle (used for the DITA standard) does things:
Code: Select all
/**
* The unique attributes recognizer
*/
private DITAUniqueAttributesRecognizer uniqueAttributesRecognizer;
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createAuthorExtensionStateListener()
*/
@Override
public AuthorExtensionStateListener createAuthorExtensionStateListener() {
uniqueAttributesRecognizer = new DITAUniqueAttributesRecognizer();
return uniqueAttributesRecognizer;
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#getClipboardFragmentProcessor()
*/
@Override
public ClipboardFragmentProcessor getClipboardFragmentProcessor() {
return uniqueAttributesRecognizer;
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#getUniqueAttributesIdentifier()
*/
@Override
public UniqueAttributesRecognizer getUniqueAttributesIdentifier() {
return uniqueAttributesRecognizer;
}
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
Hi Radu,
With Johann, we have implemented your solution but it seems that the framework doesn't call the method "createAuthorExtensionStateListener()" automatically and when we make a "paste" the method "getClipboardFragmentProcessor()" returns null.
Can you explain us how the method "createAuthorExtensionStateListener()" should be call.
Thanks.
Regards,
Isabelle
With Johann, we have implemented your solution but it seems that the framework doesn't call the method "createAuthorExtensionStateListener()" automatically and when we make a "paste" the method "getClipboardFragmentProcessor()" returns null.
Can you explain us how the method "createAuthorExtensionStateListener()" should be call.
Thanks.
Regards,
Isabelle
Re: Unique ID from copy / paste
Hi Isabelle,
Could you double check, edit the framework configuration and in the Extensions tab make sure that you did not add an implementation of AuthorExtensionStateListener as a separate extension?
Because if you add it as a separate extension it will take precedence and the alternative createAuthorExtensionStateListener() method from the ExtensionsBundle implementation will not get called anymore.
Regards,
Radu
Could you double check, edit the framework configuration and in the Extensions tab make sure that you did not add an implementation of AuthorExtensionStateListener as a separate extension?
Because if you add it as a separate extension it will take precedence and the alternative createAuthorExtensionStateListener() method from the ExtensionsBundle implementation will not get called anymore.
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
Radu,
You were right, we already had an implementation of AuthorExtensionStateListener in the framework.
It was for a StylesFilter.
So I delete this implementation and I have override the method "createAuthorStylesFilter()" in our extensions bundle implementation.
But this method is never called and our custom StylesFilter doesn't work anymore while our custom DefaultUniqueAttributesRecognizer works perfectly now.
What should I add to make both work again ?
Thanks.
Regards,
Isabelle
You were right, we already had an implementation of AuthorExtensionStateListener in the framework.
It was for a StylesFilter.
So I delete this implementation and I have override the method "createAuthorStylesFilter()" in our extensions bundle implementation.
But this method is never called and our custom StylesFilter doesn't work anymore while our custom DefaultUniqueAttributesRecognizer works perfectly now.
What should I add to make both work again ?
Thanks.
Regards,
Isabelle
Re: Unique ID from copy / paste
Hi Isabelle,
Possibly it's the same problem, in the Extensions tab you need to remove the reference to the custom styles filter implementation in order for the styles filter implementation used in the extensions bundle to start to be used.
Regards,
Radu
Possibly it's the same problem, in the Extensions tab you need to remove the reference to the custom styles filter implementation in order for the styles filter implementation used in the extensions bundle to start to be used.
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
Hello everyone,
we have still issues to implement exactly what we need.
I recap, in the same framework, we need an implementation of the StylesFilter class and an implementation of the DefaultUniqueAttributesRecognizer class. Both of these classes have to implement the AuthorExtensionStateListener (already the case for the DefaultUniqueAttributesRecognizer) because we need to use the authorAccess.
So, we have created en implementation of ExtensionsBundle and we put this in our framework file :
In this class, we have this :
With this code, when the document is opened, two instances of MyExtensionsBundle are created. The first one creates the StylesFilter, the second one creates the AuthorExtensionStateListener so it's impossible to pass the authorAccess to the StylesFilter. How can we reach what we need : both StylesFilter and DefaultUniqueAttributesRecognizer accessing authorAccess ?
Thank you for your help,
Johann
we have still issues to implement exactly what we need.
I recap, in the same framework, we need an implementation of the StylesFilter class and an implementation of the DefaultUniqueAttributesRecognizer class. Both of these classes have to implement the AuthorExtensionStateListener (already the case for the DefaultUniqueAttributesRecognizer) because we need to use the authorAccess.
So, we have created en implementation of ExtensionsBundle and we put this in our framework file :
Code: Select all
<field name="extensionsBundleClassName">
<String>package.MyExtensionsBundle</String>
</field>
In this class, we have this :
Code: Select all
@Override
public AuthorExtensionStateListener createAuthorExtensionStateListener() {
uniqueAttributesRecognizer = new myDefaultUniqueAttributesRecognizer();
return uniqueAttributesRecognizer;
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createAuthorStylesFilter()
*/
@Override
public MyStylesFilter createAuthorStylesFilter() {
if(stylesFilter == null)
{
stylesFilter = new myStylesFilter();
}
// Workaround to initialize authorAccess in StylesFilter.
// At the moment only one StateListener can be created.
if(uniqueAttributesRecognizer != null && uniqueAttributesRecognizer.getAuthorAccess() != null){
stylesFilter.activated(uniqueAttributesRecognizer.getAuthorAccess());
}
return stylesFilter;
}
public ClipboardFragmentProcessor getClipboardFragmentProcessor() {
return uniqueAttributesRecognizer;
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#getUniqueAttributesIdentifier()
*/
@Override
public MyDefaultUniqueAttributesRecognizer getUniqueAttributesIdentifier() {
return uniqueAttributesRecognizer;
}
With this code, when the document is opened, two instances of MyExtensionsBundle are created. The first one creates the StylesFilter, the second one creates the AuthorExtensionStateListener so it's impossible to pass the authorAccess to the StylesFilter. How can we reach what we need : both StylesFilter and DefaultUniqueAttributesRecognizer accessing authorAccess ?
Thank you for your help,
Johann
Re: Unique ID from copy / paste
Hi Johann,
This is more of a question about how to delegate the same event to two objects. You need a third object to act as a proxy.
Here's one possible solution:
There is also this API class ro.sync.ecss.extensions.api.AuthorExtensionStateListenerDelegator which implements AuthorExtensionStateListener and to which you can add additional AuthorExtensionStateListener listeners. It's basically the same idea.
Regards,
Radu
This is more of a question about how to delegate the same event to two objects. You need a third object to act as a proxy.
Here's one possible solution:
Code: Select all
public class MyExtensionsBundle extends ExtensionsBundle implements AuthorExtensionStateListener {
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createAuthorExtensionStateListener()
*/
@Override
public AuthorExtensionStateListener createAuthorExtensionStateListener() {
return this;
}
private StylesFilter myStylesFilter = null;
private UniqueAttributesRecognizer attributesRecognizer = null;
private AuthorAccess authorAccess;
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createAuthorStylesFilter()
*/
@Override
public StylesFilter createAuthorStylesFilter() {
if(myStylesFilter == null){
myStylesFilter = new StylesFilterTest();
if(authorAccess != null){
myStylesFilter.activated(authorAccess);
}
}
return myStylesFilter;
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#getUniqueAttributesIdentifier()
*/
@Override
public UniqueAttributesRecognizer getUniqueAttributesIdentifier() {
if(attributesRecognizer == null){
attributesRecognizer = new UniqueAttributesRecognizerTest();
if(attributesRecognizer != null){
attributesRecognizer.activated(authorAccess);
}
}
return attributesRecognizer;
}
/**
* @see ro.sync.ecss.extensions.api.AuthorExtensionStateListener#activated(ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public void activated(AuthorAccess authorAccess) {
this.authorAccess = authorAccess;
if(myStylesFilter != null){
myStylesFilter.activated(authorAccess);
}
if(attributesRecognizer != null){
attributesRecognizer.activated(authorAccess);
}
}
/**
* @see ro.sync.ecss.extensions.api.AuthorExtensionStateListener#deactivated(ro.sync.ecss.extensions.api.AuthorAccess)
*/
@Override
public void deactivated(AuthorAccess authorAccess) {
if(myStylesFilter != null){
myStylesFilter.deactivated(authorAccess);
}
if(attributesRecognizer != null){
attributesRecognizer.deactivated(authorAccess);
}
}
/**
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
*/
@Override
public String getDescription() {
return "bla";
}
/**
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#getDocumentTypeID()
*/
@Override
public String getDocumentTypeID() {
return "bla";
}
}
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
Hello Radu,
I just tried your first solution but I get the same issue because two different instances of ExtensionsBundle are created. The first one calls createAuthorExtensionStateListener and the second one calls createAuthorStylesFilter... So, the first ExtensionsBundle is not aware of the stylesFilter end the second one is not aware of the stateListener.
I am going to see your second solution.
Johann
I just tried your first solution but I get the same issue because two different instances of ExtensionsBundle are created. The first one calls createAuthorExtensionStateListener and the second one calls createAuthorStylesFilter... So, the first ExtensionsBundle is not aware of the stylesFilter end the second one is not aware of the stateListener.
I am going to see your second solution.
Johann
Re: Unique ID from copy / paste
Hi Johann,
I tested and you are right, somehow the styles filter is created very early and there is some kind of transitory time in which it is created with an extensions bundle, then another extensions bundle is created and used later on. I need to look more into this.
Can you tell me more about your use case, why do you need the AuthorAccess?
One possibility on the styles filter filter callback would be to get access to the current page something like this:
The authorPage may be null during an initial number of calls but afterwards it should return the proper API.
Regards,
Radu
I tested and you are right, somehow the styles filter is created very early and there is some kind of transitory time in which it is created with an extensions bundle, then another extensions bundle is created and used later on. I need to look more into this.
Can you tell me more about your use case, why do you need the AuthorAccess?
One possibility on the styles filter filter callback would be to get access to the current page something like this:
Code: Select all
WSEditor currented = PluginWorkspaceProvider.getPluginWorkspace().getCurrentEditorAccess(PluginWorkspace.MAIN_EDITING_AREA);
if(currented != null){
WSEditorPage currentPage = currented.getCurrentPage();
if(currentPage instanceof WSAuthorEditorPage){
WSAuthorEditorPage authorPage = (WSAuthorEditorPage) currentPage;
if(authorPage != null){
//This is the equivalent of AuthorAccess
}
}
}
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
Hi Radu,
I'm glad you got the same behaviour.
Among other things, the authorAccess in the filter method enables me to refresh some AuthorNodes.
I tried your solution and it seems to do the job.
Thank you !
Johann
I'm glad you got the same behaviour.
Among other things, the authorAccess in the filter method enables me to refresh some AuthorNodes.
I tried your solution and it seems to do the job.
Thank you !
Johann
Re: Unique ID from copy / paste
Hi Johann,
For Oxygen 19 I will try to add an improvement so that if your StylesFilter implementation also implements AuthorExtensionStateListener, our call will automatically call the activated method on your implementation with the proper authoraccess object before the styles filter implementation starts getting used.
The StylesFilter is a CSS-like layer used to style content, it should not have any other logic, for example to refresh nodes. The AuthorAccess API has lots of methods, it can modify content, it can save the current file, but this is API not suitable to be used from an interface (StylesFilter) which is a CSS-like layer.
I'm not sure how you decide to refresh nodes (maybe you could give me more details about that) but you can also add a document listener to listen for modification events and call the refresh nodes API when appropriate.
Regards,
Radu
For Oxygen 19 I will try to add an improvement so that if your StylesFilter implementation also implements AuthorExtensionStateListener, our call will automatically call the activated method on your implementation with the proper authoraccess object before the styles filter implementation starts getting used.
That's precisely why I have always been skeptical about adding a dependency betwee the StylesFilter and a very powerful API which can do all sorts of things.Among other things, the authorAccess in the filter method enables me to refresh some AuthorNodes.
The StylesFilter is a CSS-like layer used to style content, it should not have any other logic, for example to refresh nodes. The AuthorAccess API has lots of methods, it can modify content, it can save the current file, but this is API not suitable to be used from an interface (StylesFilter) which is a CSS-like layer.
I'm not sure how you decide to refresh nodes (maybe you could give me more details about that) but you can also add a document listener to listen for modification events and call the refresh nodes API when appropriate.
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Re: Unique ID from copy / paste
I understand what you mean.
We essentially use the refresh method because we set pseudoClasses to some authorNodes. Without this refresh, the pseudo class does not seem to be well applied.
Johann
We essentially use the refresh method because we set pseudoClasses to some authorNodes. Without this refresh, the pseudo class does not seem to be well applied.
Johann
Re: Unique ID from copy / paste
Hi Johann,
A pseudo class should be set on an AuthorElement from the live edited XML document only using the AuthorDocumentController API:
ro.sync.ecss.extensions.api.AuthorDocumentController.setPseudoClass(String, AuthorElement)
The API takes care of notifying the UI of the pseudo class changes.
You should normally use ro.sync.ecss.extensions.api.AuthorElementBaseInterface.setPseudoClass(String) only when the element is not part of the edited content, for example if you have created an author document fragment with AuthorDocumentController.createDocumentFragment(int, int) and you want to process the fragment before inserting it back in the document.
Regards,
Radu
A pseudo class should be set on an AuthorElement from the live edited XML document only using the AuthorDocumentController API:
ro.sync.ecss.extensions.api.AuthorDocumentController.setPseudoClass(String, AuthorElement)
The API takes care of notifying the UI of the pseudo class changes.
You should normally use ro.sync.ecss.extensions.api.AuthorElementBaseInterface.setPseudoClass(String) only when the element is not part of the edited content, for example if you have created an author document fragment with AuthorDocumentController.createDocumentFragment(int, int) and you want to process the fragment before inserting it back in the document.
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
<oXygen/> XML Editor
http://www.oxygenxml.com
Return to “SDK-API, Frameworks - Document Types”
Jump to
- Oxygen XML Editor/Author/Developer
- ↳ Feature Request
- ↳ Common Problems
- ↳ DITA (Editing and Publishing DITA Content)
- ↳ SDK-API, Frameworks - Document Types
- ↳ DocBook
- ↳ TEI
- ↳ XHTML
- ↳ Other Issues
- Oxygen XML Web Author
- ↳ Feature Request
- ↳ Common Problems
- Oxygen Content Fusion
- ↳ Feature Request
- ↳ Common Problems
- Oxygen JSON Editor
- ↳ Feature Request
- ↳ Common Problems
- Oxygen PDF Chemistry
- ↳ Feature Request
- ↳ Common Problems
- Oxygen Feedback
- ↳ Feature Request
- ↳ Common Problems
- Oxygen XML WebHelp
- ↳ Feature Request
- ↳ Common Problems
- XML
- ↳ General XML Questions
- ↳ XSLT and FOP
- ↳ XML Schemas
- ↳ XQuery
- NVDL
- ↳ General NVDL Issues
- ↳ oNVDL Related Issues
- XML Services Market
- ↳ Offer a Service