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

General cleanup of schemas and schema parsing #211

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions common/schema/config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/** Bundled HED schema configuration. */

export const localSchemaList = new Map([
['HED8.0.0', require('../../data/HED8.0.0.xml')],
['HED8.1.0', require('../../data/HED8.1.0.xml')],
['HED8.2.0', require('../../data/HED8.2.0.xml')],
['HED8.3.0', require('../../data/HED8.3.0.xml')],
['HED_score_1.0.0', require('../../data/HED_score_1.0.0.xml')],
['HED_score_1.1.0', require('../../data/HED_score_1.1.0.xml')],
['HED_score_2.0.0', require('../../data/HED_score_2.0.0.xml')],
['HED_testlib_1.0.2', require('../../data/HED_testlib_1.0.2.xml')],
['HED_testlib_2.0.0', require('../../data/HED_testlib_2.0.0.xml')],
['HED8.0.0', require('../../data/schemas/HED8.0.0.xml')],
['HED8.1.0', require('../../data/schemas/HED8.1.0.xml')],
['HED8.2.0', require('../../data/schemas/HED8.2.0.xml')],
['HED8.3.0', require('../../data/schemas/HED8.3.0.xml')],
['HED_score_1.0.0', require('../../data/schemas/HED_score_1.0.0.xml')],
['HED_score_1.1.0', require('../../data/schemas/HED_score_1.1.0.xml')],
['HED_score_2.0.0', require('../../data/schemas/HED_score_2.0.0.xml')],
['HED_testlib_1.0.2', require('../../data/schemas/HED_testlib_1.0.2.xml')],
['HED_testlib_2.0.0', require('../../data/schemas/HED_testlib_2.0.0.xml')],
])
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion validator/event/special.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import specialTags from './specialTags.json'
import specialTags from '../../data/json/specialTags.json'
import { ParsedHedGroup } from '../../parser/parsedHedGroup'
import { ParsedHedTag } from '../../parser/parsedHedTag'

Expand Down
2 changes: 1 addition & 1 deletion validator/event/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Schemas } from '../../common/schema/types'
const NAME_CLASS_REGEX = /^[\w\-\u0080-\uFFFF]+$/
const uniqueType = 'unique'
const requiredType = 'required'
const specialTags = require('./specialTags.json')
const specialTags = require('../../data/json/specialTags.json')

// Validation tests
/**
Expand Down
139 changes: 106 additions & 33 deletions validator/schema/hed3.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
} from './types'
import { generateIssue, IssueError } from '../../common/issues/issues'

const specialTags = require('../../data/json/specialTags.json')

const lc = (str) => str.toLowerCase()

export class Hed3SchemaParser extends SchemaParser {
Expand Down Expand Up @@ -108,22 +110,19 @@ export class Hed3SchemaParser extends SchemaParser {
this.properties = new Map()
for (const definition of propertyDefinitions) {
const propertyName = this.getElementTagName(definition)
if (
this._versionDefinitions.categoryProperties &&
this._versionDefinitions.categoryProperties.has(propertyName)
) {
if (this._versionDefinitions.categoryProperties?.has(propertyName)) {
this.properties.set(
propertyName,
// TODO: Switch back to class constant once upstream bug is fixed.
new SchemaProperty(propertyName, 'categoryProperty'),
)
} else if (this._versionDefinitions.typeProperties && this._versionDefinitions.typeProperties.has(propertyName)) {
} else if (this._versionDefinitions.typeProperties?.has(propertyName)) {
this.properties.set(
propertyName,
// TODO: Switch back to class constant once upstream bug is fixed.
new SchemaProperty(propertyName, 'typeProperty'),
)
} else if (this._versionDefinitions.roleProperties && this._versionDefinitions.roleProperties.has(propertyName)) {
} else if (this._versionDefinitions.roleProperties?.has(propertyName)) {
this.properties.set(
propertyName,
// TODO: Switch back to class constant once upstream bug is fixed.
Expand All @@ -144,7 +143,7 @@ export class Hed3SchemaParser extends SchemaParser {
if (propertyElements === undefined) {
properties = []
} else {
properties = propertyElements.map((element) => this.properties.get(element.name[0]._))
properties = propertyElements.map((element) => this.properties.get(this.getElementTagName(element)))
}
this.attributes.set(attributeName, new SchemaAttribute(attributeName, properties))
}
Expand Down Expand Up @@ -206,8 +205,46 @@ export class Hed3SchemaParser extends SchemaParser {
return unitClassUnits
}

// Tag parsing

/**
* Parse the schema's tags.
*/
parseTags() {
const tags = this.getAllTags()
const shortTags = this._getShortTags(tags)
const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseAttributeElements(
tags.keys(),
(element) => shortTags.get(element),
)

const tagUnitClassDefinitions = this._processTagUnitClasses(shortTags, valueAttributeDefinitions)
this._processRecursiveAttributes(shortTags, booleanAttributeDefinitions)

const tagEntries = this._createSchemaTags(
booleanAttributeDefinitions,
valueAttributeDefinitions,
tagUnitClassDefinitions,
)

this._injectTagFields(tags, shortTags, tagEntries)

const longNameTagEntries = new Map()
for (const tag of tagEntries.values()) {
longNameTagEntries.set(lc(tag.longName), tag)
}

this.tags = new SchemaTagManager(tagEntries, longNameTagEntries)
}

/**
* Generate the map from tag elements to shortened tag names.
*
* @param {Map<Object, string>} tags The map from tag elements to tag strings.
* @returns {Map<Object, string>} The map from tag elements to shortened tag names.
* @private
*/
_getShortTags(tags) {
const shortTags = new Map()
for (const tagElement of tags.keys()) {
const shortKey =
Expand All @@ -216,18 +253,22 @@ export class Hed3SchemaParser extends SchemaParser {
: this.getElementTagName(tagElement)
shortTags.set(tagElement, shortKey)
}
const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseAttributeElements(
tags.keys(),
(element) => shortTags.get(element),
)
return shortTags
}

const recursiveAttributes = this._getRecursiveAttributes()
/**
* Process unit classes in tags.
*
* @param {Map<Object, string>} shortTags The map from tag elements to shortened tag names.
* @param {Map<string, Map<SchemaAttribute, *>>} valueAttributeDefinitions The map from shortened tag names to their value schema attributes.
* @returns {Map<string, SchemaUnitClass[]>} The map from shortened tag names to their unit classes.
* @private
*/
_processTagUnitClasses(shortTags, valueAttributeDefinitions) {
const tagUnitClassAttribute = this.attributes.get('unitClass')
const tagTakesValueAttribute = this.attributes.get('takesValue')

const tagUnitClassDefinitions = new Map()
const recursiveChildren = new Map()
for (const [tagElement, tagName] of shortTags) {

for (const tagName of shortTags.values()) {
const valueAttributes = valueAttributeDefinitions.get(tagName)
if (valueAttributes.has(tagUnitClassAttribute)) {
tagUnitClassDefinitions.set(
Expand All @@ -238,53 +279,85 @@ export class Hed3SchemaParser extends SchemaParser {
)
valueAttributes.delete(tagUnitClassAttribute)
}
}

return tagUnitClassDefinitions
}

/**
* Process recursive schema attributes.
*
* @param {Map<Object, string>} shortTags The map from tag elements to shortened tag names.
* @param {Map<string, Set<SchemaAttribute>>} booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. Passed by reference.
* @private
*/
_processRecursiveAttributes(shortTags, booleanAttributeDefinitions) {
const recursiveAttributes = this._getRecursiveAttributes()

for (const [tagElement, tagName] of shortTags) {
for (const attribute of recursiveAttributes) {
const children = recursiveChildren.get(attribute) ?? []
if (booleanAttributeDefinitions.get(tagName).has(attribute)) {
children.push(...this.getAllChildTags(tagElement))
for (const childTag of this.getAllChildTags(tagElement)) {
const childTagName = this.getElementTagName(childTag)
booleanAttributeDefinitions.get(childTagName).add(attribute)
}
}
recursiveChildren.set(attribute, children)
}
}

for (const [attribute, childTagElements] of recursiveChildren) {
for (const tagElement of childTagElements) {
const tagName = this.getElementTagName(tagElement)
booleanAttributeDefinitions.get(tagName).add(attribute)
}
}
}

/**
* Create the {@link SchemaTag} objects.
*
* @param {Map<string, Set<SchemaAttribute>>} booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes.
* @param {Map<string, Map<SchemaAttribute, *>>} valueAttributeDefinitions The map from shortened tag names to their value schema attributes.
* @param {Map<string, SchemaUnitClass[]>} tagUnitClassDefinitions The map from shortened tag names to their unit classes.
* @returns {Map<string, SchemaTag>} The map from lowercase shortened tag names to their tag objects.
* @private
*/
_createSchemaTags(booleanAttributeDefinitions, valueAttributeDefinitions, tagUnitClassDefinitions) {
const tagTakesValueAttribute = this.attributes.get('takesValue')
const tagEntries = new Map()

for (const [name, valueAttributes] of valueAttributeDefinitions) {
if (tagEntries.has(name)) {
IssueError.generateAndThrow('duplicateTagsInSchema')
}

const booleanAttributes = booleanAttributeDefinitions.get(name)
const unitClasses = tagUnitClassDefinitions.get(name)

if (booleanAttributes.has(tagTakesValueAttribute)) {
tagEntries.set(lc(name), new SchemaValueTag(name, booleanAttributes, valueAttributes, unitClasses))
} else {
tagEntries.set(lc(name), new SchemaTag(name, booleanAttributes, valueAttributes, unitClasses))
}
}

return tagEntries
}

/**
* Inject special tag fields into the {@link SchemaTag} objects.
*
* @param {Map<Object, string>} tags The map from tag elements to tag strings.
* @param {Map<Object, string>} shortTags The map from tag elements to shortened tag names.
* @param {Map<string, SchemaTag>} tagEntries The map from shortened tag names to tag objects.
* @private
*/
_injectTagFields(tags, shortTags, tagEntries) {
for (const tagElement of tags.keys()) {
const tagName = shortTags.get(tagElement)
const parentTagName = shortTags.get(tagElement.$parent)

if (parentTagName) {
tagEntries.get(lc(tagName))._parent = tagEntries.get(lc(parentTagName))
}

if (this.getElementTagName(tagElement) === '#') {
tagEntries.get(lc(parentTagName))._valueTag = tagEntries.get(lc(tagName))
}
}

const longNameTagEntries = new Map()
for (const tag of tagEntries.values()) {
longNameTagEntries.set(lc(tag.longName), tag)
}

this.tags = new SchemaTagManager(tagEntries, longNameTagEntries)
}

_parseDefinitions(category) {
Expand Down
Loading