Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the fn:generate-id function #641

Merged
merged 3 commits into from
Jul 12, 2024

Conversation

egh
Copy link
Contributor

@egh egh commented May 31, 2024

https://www.w3.org/TR/xpath-functions-31/#func-generate-id

Simple implementation, but it seems to work.

Copy link

bundlemon bot commented May 31, 2024

BundleMon

Files updated (2)
Status Path Size Limits
fontoxpath.esm.js
78.54KB (+99B +0.12%) -
fontoxpath.js
78.42KB (+98B +0.12%) -

Total files change +197B +0.12%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@coveralls
Copy link

coveralls commented May 31, 2024

Coverage Status

coverage: 91.567% (+0.01%) from 91.556%
when pulling 2d9d9a5 on egh:implement-generate-id
into 35d0da5 on FontoXML:master.

@DrRataplan
Copy link
Collaborator

Thanks!!!

The test failing with a static error is this one:

  <test-case name="generate-id-018">
      <description>generate-id() applied to a sequence of element nodes in backwards-compatibility mode</description>
      <created by="Michael Kay" on="2010-12-20"/>
      <modified by="Michael Kay" on="2013-01-15" change="make dependency on XPath explicit"/>
      <environment ref="auction"/>
      <dependency type="spec" value="XP20+"/>
      <dependency type="feature" value="xpath-1.0-compatibility"/>
      <test>generate-id(//*) eq generate-id(/*)</test>
      <result>
         <assert-true/>
      </result>
   </test-case>

Sure. XPath 1.0 compatibility mode is not something we implement. Or ever will if it is up to me. Lets ignore it.

The others are stranger. I would expect them to work since they are just verifying all ids are distinct. Can you maybe look into that?

@egh
Copy link
Contributor Author

egh commented Jun 3, 2024

@DrRataplan Thank you for looking at this! I wasn't sure if those xpath features were implemented (my xpath knowledge mostly stops at 2.0 😀 ). It turns out the counter wasn't properly being passed to some children. I believe I have corrected the issue.

@egh
Copy link
Contributor Author

egh commented Jun 3, 2024

Looks like there are some type errors I need to fix...

@egh
Copy link
Contributor Author

egh commented Jun 5, 2024

I just realized that this won't really work for my use case (an xslt processor) unless I allow the caller of an evaluateXpath function to pass in a counter. I'll give that a shot...

@DrRataplan
Copy link
Collaborator

DrRataplan commented Jun 5, 2024 via email

@egh
Copy link
Contributor Author

egh commented Jun 7, 2024

Is that because you need the result to be deterministic over multiple calls? A weakmap on the module level should fit that, right? Otherwise, something on DomFacade would not be too dirty...

Yes, that's right... I think a DomFacade seems like the way to go. Thanks for the tip.

@@ -16,6 +16,8 @@ class DynamicContext {
public contextItemIndex: number;
public contextSequence: ISequence;
public variableBindings: { [s: string]: () => ISequence };
public generateIdMap: WeakMap<Node, string>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you move this weakmap to the module level, as in, make it its own file like this:

const generateIdMap: WeakMap<Node,string> = new WeakMap();
export default generateIdMap;

and just directly talk to that in the generate-id function, circumventing the DynamicContext, you will get that stability over multiple calls. Because this is a weakMap I don't expect any memory downsides. I'd like this more than externalizing this state with a DomFacade. That would require other users of FontoXPath to do some upgrading on their side, whcih I'm trying to minimize.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me! I'll give this a shot. Thank you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented the function using a module, but the WeakMap is not working as I expected with nodes. It doesn't seem to be recognizing previous nodes. I'm trying to track this down, but I would have expected nodes to be equal across subsequent calls. See the failing test here https://github.com/FontoXML/fontoxpath/actions/runs/9475980881/job/26108159273?pr=641#step:6:100

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NodePointers. Forgot about them.

In XQuery (Update Facility) we are sometimes cloning nodes, a lot. When you wrap a large structure (like let $ele := <ele/> let $wrap := <wrap>{$ele}</wrap> return $wrap) the <ele/> in $ele and the one in the $wrap are not equal ($ele is $wrap/ele is false). playground

Figuring out how to circumvent this. The nodeCompare function compares node identity, so that's close. We might have to memoize the createPointer function in DomFacade.ts here. That is the only place we make NodePointers. This way it can work over XQuery-made nodes as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to work for the tests I wrote. There are probably situations where it won't work:

import { NodePointer, TinyNode } from '../../domClone/Pointer';
import { ConcreteNode } from 'src/domFacade/ConcreteNode';

const generateIdMap: WeakMap<TinyNode | ConcreteNode, string> = new WeakMap();
let generateIdCounter = 0;

function generateId(ptr: NodePointer): string {
  const node = ptr.node;
	if (!generateIdMap.has(node)) {
		generateIdMap.set(node, `id${++generateIdCounter}`);
	}
	return generateIdMap.get(node);
}

export default generateId;

@egh egh force-pushed the implement-generate-id branch 5 times, most recently from 1d0a956 to 5f71b5b Compare June 13, 2024 00:13
Simple implementation, but it seems to work.
@egh
Copy link
Contributor Author

egh commented Jun 19, 2024

I'm pretty happy with what I have here - what do you think?

@DrRataplan
Copy link
Collaborator

DrRataplan commented Jun 19, 2024 via email

@egh
Copy link
Contributor Author

egh commented Jun 20, 2024

No problem! Thank you. I just wanted to let you know it was ready for your review. Thanks for everything.

egh added a commit to egh/xjslt that referenced this pull request Jun 21, 2024
DrRataplan
DrRataplan previously approved these changes Jun 24, 2024
Copy link
Collaborator

@DrRataplan DrRataplan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Let's merge it!

) => {
let node: NodePointer;
if (!nodeValue) {
if (!dynamicContext || !dynamicContext.contextItem) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could've used the contextItemAsFirstArgument helper, that does this internally, but I'm fine either way.

@DrRataplan
Copy link
Collaborator

Finally got around to those linting errors. Thanks again for the help!

@DrRataplan DrRataplan merged commit 6a7bd94 into FontoXML:master Jul 12, 2024
8 checks passed
@DrRataplan
Copy link
Collaborator

Since I'm no longer with Fonto I cannot release this. @bwrrp, can you hit the ship button somewhere after your holidays?

@egh egh deleted the implement-generate-id branch July 12, 2024 16:28
@egh
Copy link
Contributor Author

egh commented Jul 12, 2024

Thank you @DrRataplan ! and best of luck with the new work.

@bwrrp
Copy link
Member

bwrrp commented Aug 13, 2024

Sorry for the delay, just released 3.33.0 which includes this

@egh
Copy link
Contributor Author

egh commented Aug 13, 2024

🙏 Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants