How to insert a custom ID in newly inserted elements automatically?

Having trouble deploying Oxygen XML Web Author? Got a bug to report? Post it all here.
aujunior
Posts: 28
Joined: Thu Feb 16, 2023 11:00 pm

How to insert a custom ID in newly inserted elements automatically?

Post by aujunior »

Hi!

Is there a way to insert an element XML with the ID attribute already filled?

We are going to create a custom rule for ID creation, and we need each inserted element to be created with this custom ID.
Is there a way to invoke this method when inserting each XML element?

Or an action that is triggered during the element inclusion, via content completion?

We don't have a list of elements containing IDs. However, I know that the Oxygen can read the schema and check if the element should have an ID. If it should, we want to invoke the custom ID generation function.

I notice that there's a method that can be invoked through an action. However, we need the element to already have the ID when it's created, rather than requiring the user to click a button to trigger the ID generation action.

Could you please assist?
Radu
Posts: 9059
Joined: Fri Jul 09, 2004 5:18 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by Radu »

Hi,

Is the attribute defined as required for the XML document in the DTD or Schema associated with it?
Are you working with the XML document in the Text editing mode or in the Author visual editing mode? Do you want the ID generation for the inserted XML element to work both when editing in the Text and the Author visual editing modes?
For the Author visual editing mode we have framework customizations which would allow you to replace in the content completion window that specific element with a custom Author action which would give you better control over what to insert:
https://blog.oxygenxml.com/topics/custo ... etion.html
Other than that, a framework based extension which would work both in the Text and Author modes and take control over the content completion elements would need to be implemented in Java:
https://www.oxygenxml.com/doc/versions/ ... ndler.html

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
aujunior
Posts: 28
Joined: Thu Feb 16, 2023 11:00 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by aujunior »

Hi Radu!

Yes, the attribute is defined as required in the Schema associated.
We will work only with Author visual editing mode.

The idea is that Oxygen can intercept any element that is inserted via content completion and call a class that creates an ID for each situation.

Is there a way to make an action for all elements or do we have to map all elements in advance and create an action for each?
Radu
Posts: 9059
Joined: Fri Jul 09, 2004 5:18 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by Radu »

Hi,
Indeed by default Oxygen inserts the required attribute name but does not insert a value for it, expecting the end user to set the value themselves.
I assume you have already created a framework configuration for your XML vocabulary in the Oxygen Preferences->"Document Type Association" page.
If you edit your framework configuration, in the "Classpath" tab you can add a reference to a "resources" folder from inside your framework folder:

Code: Select all

${framework}/resources/
and then create a "resources" folder inside your framework folder. Then inside the "resources" folder create a file named "cc_config.xml" with the content:

Code: Select all

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.oxygenxml.com/ns/ccfilter/config http://www.oxygenxml.com/ns/ccfilter/config/ccConfigSchemaFilter.xsd"
	xmlns="http://www.oxygenxml.com/ns/ccfilter/config">
	<elementProposals path="yourElementName">
		<insertAttribute name="id" value="prefix_${id}"/>
	</elementProposals>
</config>
More about what the cc_config.xml can do:
https://www.oxygenxml.com/doc/versions/ ... ually.html

Basically the value uses an Oxygen editor variable named ${id} which will get expanded when the element is inserted.
There are other Oxygen editor variables which may be useful like ${uuid} if you want an even stronger (but much longer) id value.

There is yet another another way to take full control over the generated ID attributes if you are or know a Java developer who is willing to work on this.
If you edit your framework customization in the Oxygen Preferences->"Document Type Association" page, in the Classpath tab you can add a reference to a custom Java JAR library. Then in the "Extensions" tab there is an extension named "Unique attributes recognizer":
https://www.oxygenxml.com/doc/versions/ ... nizer.html

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
aujunior
Posts: 28
Joined: Thu Feb 16, 2023 11:00 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by aujunior »

Hello Radu!

Thank you so much for your answer.

We decided to use a Java operation with an extension for ExtensionsBundle.

We created the idGenerationDefaultOptions.xml file which worked great for XML without NAMESPACE declared.

But for the 2300 specification we have xmlns="http://www.ataebiz.org/XMLSchema".

In the cc_config.xml file we use <elementProposals xmlns:ata="http://www.ataebiz.org/XMLSchema" path="ata:para">.

We have to declare the NAMESPACE in the idGenerationDefaultOptions.xml file or do we have to resolve the namespace via Java in the ExtensionsBundle extended class?

How do we proceed?

Thank you!
Radu
Posts: 9059
Joined: Fri Jul 09, 2004 5:18 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by Radu »

Hi,
Did you created your own Java class which extends our base "ro.sync.ecss.extensions.commons.id.DefaultUniqueAttributesRecognizer" class?
And return this custom class on the "ro.sync.ecss.extensions.api.ExtensionsBundle.getUniqueAttributesIdentifier()" callback?
Looking at our source code in the "ro.sync.ecss.extensions.commons.id.DefaultUniqueAttributesRecognizer.getGenerateIDAttributeQName(AuthorElement, String[], boolean)" method it seems we interpret these elements specified in the "idGenerationDefaultOptions.xml" as being local names:

Code: Select all

<generateForElement>...</generateForElement>
So we are not namespace aware in this regard, for your case you would need to specify the element local name there, without any prefix like:

Code: Select all

<generateForElement>para</generateForElement>
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
aujunior
Posts: 28
Joined: Thu Feb 16, 2023 11:00 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by aujunior »

Hi Radu!

I found the problem.

We override getGenerateIDAttributeQName to check whether or not the element parameter can receive an ID depending on the Schema.

We use:
WhatAttributesCanGoHereContext attrContext = authorSchemaManager.createWhatAttributesCanGoHereContext(element);
oxyAttrList = authorSchemaManager.whatAttributesCanGoHere(attrContext);

However, the system is not returning the list of attributes of the sent element.

Using authorSchemaManager.getElementDescription(attrContext).getAttributes().toString() the system returns the attributes of the parent element and not the requested element.

Are we collecting the list of attributes wrongly or is there a problem with the schema?

Regards,
Audye
Radu
Posts: 9059
Joined: Fri Jul 09, 2004 5:18 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by Radu »

Hello Audye,
Please see some remarks below:
We override getGenerateIDAttributeQName to check whether or not the element parameter can receive an ID depending on the Schema.
I understand.
We use:
WhatAttributesCanGoHereContext attrContext = authorSchemaManager.createWhatAttributesCanGoHereContext(element);
oxyAttrList = authorSchemaManager.whatAttributesCanGoHere(attrContext);
However, the system is not returning the list of attributes of the sent element.
This should work, the Javadoc for the authorSchemaManager.whatAttributesCanGoHere states:

Code: Select all

Examines the grammar and decides what attributes can be inserted in the parent element, after the list of attributes names.
It means to say that it returns the list of possible attributes, except for the attributes which are already present on the element.
For example for an element like this:

Code: Select all

<hazardstatement id="hazardstatement_t1y_1kk_syb" type="important">
it already has the @id and @type attributes present on it so the authorSchemaManager.whatAttributesCanGoHere will return all other attributes which can be inserted, but it will not return "id" and "type" because they are already set on the element.
Using authorSchemaManager.getElementDescription(attrContext).getAttributes().toString() the system returns the attributes of the parent element and not the requested element.
The WhatAttributesCanGoHereContext is specifically built to be used with the whatAttributesCanGoHere method.
You can also use code like this if you want to obtain all possible attributes which can be added to the element:

Code: Select all

            WhatElementsCanGoHereContext ctxt =
                authorSchemaManager.createWhatElementsCanGoHereContext(element.getStartOffset() + 1);
            
            CIElement elementDescription = authorSchemaManager.getElementDescription(ctxt);
            List allAttributes = elementDescription.getAttributes();
One thing to keep in mind, calls to the authorSchemaManager do not take that much time to compute but the getGenerateIDAttributeQName may be called quite often. For example if you copy a large piece of DITA content from one topic and paste it in another the "getGenerateIDAttributeQName" will get called for all DITA XML elements in the copied content, so you may experience a slowdown if you use very frequent calls to the authorSchemaManager, maybe you could have also some kind of intermediary cache of element name mapped to possible attributes.

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
aujunior
Posts: 28
Joined: Thu Feb 16, 2023 11:00 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by aujunior »

Thank you very much Radu!

Code: Select all

WhatElementsCanGoHereContext ctxt = author SchemaManager.create WhatElementsCanGoHereContext(element.getStartOffset() + 1);
It solved the whole problem.

We will take into account what you mentioned about the performance of copying and pasting content.

I have one last doubt at the moment.

Is there any way to call the ExtensionBundle ID creation method when opening the document for editing?

When opening the XML document for editing, we would like Oxygen to scan all the elements that are in the XML in order to generate the missing IDs.

Is there any way to place a listener via WEB or JAVA in our framework to carry out this ID generation activity?
Radu
Posts: 9059
Joined: Fri Jul 09, 2004 5:18 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by Radu »

Hi,
Maybe something like this might work, but it would need to be tested:

Code: Select all

public class MyExtensionsBundle extends ExtensionsBundle {
  
  /**
   * @see ro.sync.ecss.extensions.api.ExtensionsBundle#createAuthorExtensionStateListener()
   */
  @Override
  public AuthorExtensionStateListener createAuthorExtensionStateListener() {
    return new AuthorExtensionStateListener() {
      @Override
      public String getDescription() {
        return null;
      }
      
      @Override
      public void deactivated(AuthorAccess authorAccess) {
      }
      
      @Override
      public void activated(AuthorAccess authorAccess) {
        //This is called when an XML is opened in the Author visual editing mode
        //or when the XML is switched from the Text mode to the Author visual editing mode
        getUniqueAttributesIdentifier().assignUniqueIDs(0, authorAccess.getDocumentController().getAuthorDocumentNode().getLength(), false);
      }
    };
  }
  
  /**
   * @see ro.sync.ecss.extensions.api.ExtensionsBundle#getUniqueAttributesIdentifier()
   */
  @Override
  public UniqueAttributesRecognizer getUniqueAttributesIdentifier() {
    return new MyUniqueAttrsIdentifier();
  }
Also the document will appear as modified to the user if you make changes to it after it is loaded.

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
aujunior
Posts: 28
Joined: Thu Feb 16, 2023 11:00 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by aujunior »

Hello Radu!

Your suggestion worked perfectly!

It is already tested and confirmed.

The only change I have to make is to call uniqueAttributesRecognizer.activated(authorAccess) first, so it loaded the methods that I changed due to our customizations.

Code: Select all

return new AuthorExtensionStateListener() {
            @Override
            public String getDescription() {
                return uniqueAttributesRecognizer.getDescription();
            }

            /**
             * @see ro.sync.ecss.extensions.api.AuthorExtensionStateListener#deactivated(ro.sync.ecss.extensions.api.AuthorAccess)
             */
            @Override
            public void deactivated(AuthorAccess authorAccess) {
                uniqueAttributesRecognizer.deactivated(authorAccess);
            }

            /**
             * @see ro.sync.ecss.extensions.api.AuthorExtensionStateListener#activated(ro.sync.ecss.extensions.api.AuthorAccess)
             */
            @Override
            public void activated(AuthorAccess authorAccess) {
               //have to call activated before
                uniqueAttributesRecognizer.activated(authorAccess);
                uniqueAttributesRecognizer.assignUniqueIDs(0, authorAccess.getDocumentController().getAuthorDocumentNode().getLength(), false);
            }
}
I saw that using the cc_config.xml file we were able to create generation patterns for more than one attribute.

Code: Select all

<elementProposals path="para">
        <insertAttribute name="id" value="${xpath_eval(/*/@id)}_${uuid}"/>
        <insertAttribute name="idCustom" value="CUSTOM_${uuid}"/>
    </elementProposals>

By extensibleBundle we have only 1 option in the idGenerationDefaultOptions.xml file which is the <idAttribute>id</idAttribute> entry.
Is there a way to use extensibleBundle to automatically generate the ID and another attribute on the same element?
Radu
Posts: 9059
Joined: Fri Jul 09, 2004 5:18 pm

Re: How to insert a custom ID in newly inserted elements automatically?

Post by Radu »

Hi,
Please see some more remarks below:
The only change I have to make is to call uniqueAttributesRecognizer.activated(authorAccess) first, so it loaded the methods that I changed due to our customizations.
Right
I saw that using the cc_config.xml file we were able to create generation patterns for more than one attribute.
Yes, and also you can use your own custom editor variables like ${customID} for which you can add a custom editor variables resolver:
ro.sync.exml.workspace.api.util.UtilAccess.addCustomEditorVariablesResolver(EditorVariablesResolver)
https://www.oxygenxml.com/InstData/Edit ... sResolver-
By extensibleBundle we have only 1 option in the idGenerationDefaultOptions.xml file which is the <idAttribute>id</idAttribute> entry.
Is there a way to use extensibleBundle to automatically generate the ID and another attribute on the same element?
Instead of extending the DefaultUniqueAttributesRecognizer which is based on that XML configuration file which is more rigid than what you want you could implement directly the "ro.sync.ecss.extensions.api.UniqueAttributesRecognizer" interface and implement the "assignUniqueIDs" method from the ground up.
The complete Java source code for the "ro.sync.ecss.extensions.commons.id.DefaultUniqueAttributesRecognizer" should be in our Maven SDK:
https://www.oxygenxml.com/oxygen_sdk.html

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
Post Reply