Skip to content

[import] A qualified name containing a non-ASCII character never resolves #2554

Description

@kkkk1258999

Summary

A qualified name whose qualification contains a non-ASCII character never resolves. Namespace#qualificationOf passes the qualification through StringEscapeUtils.escapeJava, which turns every non-ASCII character into a \uXXXX sequence, and Namespace#resolve then looks that mangled string up against the real element names.

Models written in a language other than English cannot use qualified names to refer to their own elements.

Version

  • Reproduced on SysON 2026.9.2
  • The code is unchanged on main (b380dada4); line numbers below are from main

Steps to reproduce

  1. In any project, insert this text. The two packages differ only in whether the name is ASCII:
package Ascii {
    part def Thing;
}
package Café {
    part def Thing;
}
part viaAscii : Ascii::Thing;
part viaNonAscii : Café::Thing;
  1. Read the report of the import.

Actual

Ascii::Thing resolves. Café::Thing does not, and the import reports:

Unable to resolve name 'Café::Thing' for reference 'type'
on element '[FeatureTyping] Batmobile::viaNonAscii::<FeatureTyping>'

The FeatureTyping is left without a type.

Expected

Both resolve. Nothing in the SysML v2 textual notation restricts a name to ASCII, and the element is present in the model under exactly that name.

Where

NamespaceImpl.java on main:

// 248
public String qualificationOf(String qualifiedName) {
    List<String> segments = NameHelper.parseQualifiedName(qualifiedName);
    if (segments.size() < 2) {
        return null;
    } else {
        String qualification = String.join("::", segments.subList(0, segments.size() - 1));
        return NameHelper.escapeString(qualification);
    }
}

// 269
public Membership resolve(String qualifiedName) {
    String qualification = this.qualificationOf(qualifiedName);
    String name = this.unqualifiedNameOf(qualifiedName);
    Membership result = null;
    if (qualification == null) {
        result = this.resolveLocal(name);
    } else {
        Membership membership = this.resolve(qualification);
        ...

NameHelper#escapeString is StringEscapeUtils.escapeJava. Running it over the names above gives:

Ascii       -> Ascii
Café        -> Café
日本語       -> 日本語
Ascii::Sub  -> Ascii::Sub
Café::Sub   -> Café::Sub

So for an ASCII name escapeJava is the identity and nothing is noticed. For any other name, resolve recurses on a string that no element is called, the recursion bottoms out in resolveLocal with no match, and the whole qualified name fails.

The two halves are also not inverses of each other: unqualifiedNameOf calls NameHelper#unescapeString, which only strips surrounding single quotes, while qualificationOf calls escapeString. Whatever escapeString is there for, the qualification and the unqualified name are not treated the same way.

Scale

In a model of ours with Japanese package and element names, 1 of 126 qualified names resolved. The same model with the names transliterated to ASCII resolved 126 of 126. The one that resolved had no qualification, so it never reached qualificationOf.

This also affects accented Latin names, so it is not limited to non-Latin scripts.

Suggested direction

escapeString looks like it belongs to the textual export, where a name may need quoting, rather than to name resolution, where the string has to match an element name exactly. Removing it from qualificationOf is the obvious change, but I do not know what it was protecting against, so I would rather leave the decision to you than send a patch that quietly drops it.

If it is needed for some inputs, then resolve has to compare escaped against escaped, or unescape before comparing — at the moment one side is escaped and the other is not.

Out of scope

  • The quoting of names in the textual export.
  • unqualifiedNameOf and unescapeString, except for the asymmetry noted above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions