twink/vendor/sourcery.dny.nu/longdistance/compact.go
2025-12-05 12:20:05 +01:00

1410 lines
32 KiB
Go

package longdistance
import (
"cmp"
"fmt"
"maps"
"slices"
"strings"
"sourcery.dny.nu/longdistance/internal/json"
"sourcery.dny.nu/longdistance/internal/url"
)
func (p *Processor) compactIRI(
activeContext *Context,
key string,
value any,
vocab bool,
reverse bool,
) (string, error) {
// 1)
if key == "" {
return "", nil
}
// this should be done as the first thing in step 10
// but we can avoid a ton of work by doing it early here
if strings.HasPrefix(key, BlankNode) {
return key, nil
}
if slices.Contains(p.excludeIRIsFromCompaction, key) {
return key, nil
}
// 2)
activeContext.initInverse()
// 3)
inverse := activeContext.inverse
object, isObject := value.(Node)
// 4)
if _, ok := inverse[key]; ok && vocab {
// 4.1)
defaultLanguage := KeywordNone // 4.1.2)
if activeContext.defaultDirection != "" {
// 4.1.1)
defaultLanguage = activeContext.defaultLang + "_" + activeContext.defaultDirection
} else if activeContext.defaultLang != "" {
// 4.1.2)
defaultLanguage = "_" + activeContext.defaultLang
}
// 4.2) we don't have @preserve
// if value != nil && value.Preserve != nil {}
// 4.3)
containers := make([]string, 0, 4)
// 4.4)
typeLanguage := KeywordLanguage
typeLanguageValue := KeywordNull
// 4.5)
if isObject && object.Has(KeywordIndex) && !object.IsGraph() {
containers = append(containers,
KeywordIndex,
KeywordIndex+KeywordSet,
)
}
if reverse {
// 4.6)
typeLanguage = KeywordType
typeLanguageValue = KeywordReverse
containers = append(containers, KeywordSet)
} else if isObject && object.IsList() {
// 4.7)
// 4.7.1)
if !object.Has(KeywordIndex) {
containers = append(containers, KeywordList)
}
// 4.7.2) don't need it
// 4.7.3)
var commonLanguage *string
var commonType *string
if len(object.List) == 0 {
commonLanguage = &defaultLanguage
}
// 4.7.4)
for _, item := range object.List {
// 4.7.4.1)
itemLanguage := KeywordNone
itemType := KeywordNone
// 4.7.4.2)
if item.IsValue() {
if item.Has(KeywordDirection) {
if item.Has(KeywordLanguage) {
itemLanguage = item.Language + "_" + item.Direction
} else {
itemLanguage = "_" + item.Direction
}
} else if item.Has(KeywordLanguage) {
itemLanguage = item.Language
} else if item.Has(KeywordType) {
itemType = item.Type[0]
} else {
itemLanguage = KeywordNull
}
} else {
// 4.7.4.3)
itemType = KeywordID
}
if commonLanguage == nil {
// 4.7.4.4)
commonLanguage = &itemLanguage
} else if itemLanguage != *commonLanguage &&
isObject && object.Value != nil {
// 4.7.4.5)
*commonLanguage = KeywordNone
}
if commonType == nil {
// 4.7.4.6)
commonType = &itemType
} else if itemType != *commonType {
// 4.7.4.7)
*commonType = KeywordNone
}
// 4.7.4.8)
if commonLanguage != nil && commonType != nil &&
*commonLanguage == KeywordNone &&
*commonType == KeywordNone {
break
}
}
// 4.7.5)
if commonLanguage == nil {
commonLanguage = new(string)
*commonLanguage = KeywordNone
}
// 4.7.6)
if commonType == nil {
commonType = new(string)
*commonType = KeywordNone
}
if *commonType != KeywordNone {
// 4.7.7)
typeLanguage = KeywordType
typeLanguageValue = *commonType
} else {
// 4.7.8)
typeLanguageValue = *commonLanguage
}
} else if isObject && object.IsGraph() {
// 4.8)
if object.Has(KeywordIndex) {
// 4.8.1)
containers = append(containers,
KeywordGraph+KeywordIndex,
KeywordGraph+KeywordIndex+KeywordSet,
)
}
if object.Has(KeywordID) {
// 4.8.2)
containers = append(containers,
KeywordGraph+KeywordID,
KeywordGraph+KeywordID+KeywordSet,
)
}
// 4.8.3)
containers = append(containers,
KeywordGraph,
KeywordGraph+KeywordSet,
KeywordSet,
)
if !object.Has(KeywordIndex) {
// 4.8.4)
containers = append(containers,
KeywordGraph+KeywordIndex,
KeywordGraph+KeywordIndex+KeywordSet,
)
}
if !object.Has(KeywordID) {
// 4.8.5)
containers = append(containers,
KeywordGraph+KeywordID,
KeywordGraph+KeywordID+KeywordSet,
)
}
// 4.8.6)
containers = append(containers,
KeywordIndex,
KeywordIndex+KeywordSet,
)
typeLanguage = KeywordType
typeLanguageValue = KeywordID
} else {
// 4.9)
if isObject && object.IsValue() {
// 4.9.1)
if object.Has(KeywordDirection) && !object.Has(KeywordIndex) {
if object.Has(KeywordLanguage) {
typeLanguageValue = object.Language + "_" + object.Direction
} else {
typeLanguageValue = "_" + object.Direction
}
containers = append(containers,
KeywordLanguage,
KeywordLanguage+KeywordSet)
} else if object.Has(KeywordLanguage) && !object.Has(KeywordIndex) {
typeLanguageValue = object.Language
containers = append(containers,
KeywordLanguage,
KeywordLanguage+KeywordSet)
} else if object.Has(KeywordType) {
typeLanguage = KeywordType
typeLanguageValue = object.Type[0]
}
} else {
// 4.9.3)
typeLanguage = KeywordType
typeLanguageValue = KeywordID
containers = append(containers,
KeywordID,
KeywordID+KeywordSet,
KeywordType,
KeywordSet+KeywordType,
)
}
// 4.9.3)
containers = append(containers, KeywordSet)
}
// 4.10)
containers = append(containers, KeywordNone)
if !p.modeLD10 {
// 4.11)
if !isObject || (isObject && !object.Has(KeywordIndex)) {
containers = append(containers,
KeywordIndex,
KeywordIndex+KeywordSet)
}
// 4.12)
if isObject && object.IsValue() && len(object.PropertySet()) == 1 {
containers = append(containers,
KeywordLanguage,
KeywordLanguage+KeywordSet)
}
}
// 4.13)
if typeLanguageValue == "" {
typeLanguageValue = KeywordNull
}
// 4.14)
preferredValues := make([]string, 0, 4)
// 4.15)
if typeLanguageValue == KeywordReverse {
preferredValues = append(preferredValues, KeywordReverse)
}
if isObject && object.Has(KeywordID) && (typeLanguageValue == KeywordID || typeLanguageValue == KeywordReverse) {
// 4.16)
c, err := p.compactIRI(
activeContext,
object.ID,
nil, true, false,
)
if err != nil {
return "", err
}
cdef, cok := activeContext.defs[c]
if cok && cdef.IRI == object.ID {
// 4.16.1)
preferredValues = append(preferredValues,
KeywordVocab,
KeywordID,
KeywordNone)
} else {
// 4.16.2)
preferredValues = append(preferredValues,
KeywordID,
KeywordVocab,
KeywordNone)
}
} else {
// 4.17)
preferredValues = append(preferredValues,
typeLanguageValue,
KeywordNone)
if isObject && object.IsList() && len(object.List) == 0 {
typeLanguage = KeywordAny
}
}
// 4.18)
preferredValues = append(preferredValues, KeywordAny)
// 4.19)
for _, p := range preferredValues[:] {
idx := strings.Index(p, "_")
if idx == -1 {
continue
}
preferredValues = append(preferredValues, p[idx:])
}
// 4.20)
term := selectTerm(
activeContext,
key,
containers,
typeLanguage,
preferredValues,
)
// 4.21)
if term != "" {
return term, nil
}
}
// 5)
vocabMapping := activeContext.vocabMapping
if vocab && vocabMapping != "" {
if strings.HasPrefix(key, vocabMapping) && len(key) > len(vocabMapping) {
// 5.1)
suffix := strings.TrimPrefix(key, vocabMapping)
if _, ok := activeContext.defs[suffix]; !ok {
return suffix, nil
}
}
}
// 6)
compactIRI := ""
// 7)
for term, def := range activeContext.defs {
if def.IRI == "" || def.IRI == key || !strings.HasPrefix(
key, def.IRI) || !def.Prefix {
// 7.1)
continue
}
// 7.2)
candidate := term + ":" + strings.TrimPrefix(
key, def.IRI)
// 7.3)
cdef, cok := activeContext.defs[candidate]
if !cok && (compactIRI == "" || sortedLeast(candidate, compactIRI) < 0) {
compactIRI = candidate
} else if cok && cdef.IRI == key && value == nil {
compactIRI = candidate
}
}
// 8)
if compactIRI != "" {
return compactIRI, nil
}
// 9)
u, err := url.Parse(key)
if err != nil {
return "", err
}
for term, def := range activeContext.defs {
if u.Scheme == term && def.Prefix && u.Host == "" {
return "", ErrIRIConfusedWithPrefix
}
}
// 10)
if !vocab && activeContext.currentBaseIRI != "" {
res, err := url.Relative(activeContext.currentBaseIRI, key)
if err == nil {
if looksLikeKeyword(res) {
res = "./" + res
}
key = res
}
}
// 11)
return key, nil
}
func (p *Processor) compactValue(
ctx *Context,
prop string,
value *Node,
) (any, error) {
// 1) 2) and 3) aren't needed
// 4)
language := ctx.defs[prop].Language
if language == "" && ctx.defaultLang != "" {
language = ctx.defaultLang
}
// 5)
direction := ctx.defs[prop].Direction
if direction == "" && ctx.defaultDirection != "" {
direction = ctx.defaultDirection
}
allProps := value.PropertySet()
allPropsLen := len(allProps)
def, defOK := ctx.defs[prop]
if (value.Has(KeywordID) && allPropsLen == 1) || (value.Has(KeywordID) && value.Has(KeywordIndex) && allPropsLen == 2) {
// 6)
if defOK && def.Type != "" {
var res string
var err error
switch def.Type {
case KeywordID:
res, err = p.compactIRI(ctx,
value.ID,
nil,
false, false)
case KeywordVocab:
res, err = p.compactIRI(ctx,
value.ID,
nil,
true, false)
default:
return nil, nil
}
return res, err
} else {
return nil, nil
}
} else if defOK && value.Has(KeywordType) && slices.Contains(value.Type, def.Type) {
// 7)
return value.Value, nil
} else if (defOK && def.Type == KeywordNone) || value.Has(KeywordType) && !slices.Contains(value.Type, def.Type) {
// 8) don't need to do anything here
return nil, nil
} else if value.IsValue() && !json.IsString(value.Value) {
// 9)
if (value.Has(KeywordIndex) && slices.Contains(def.Container, KeywordIndex)) || !value.Has(KeywordIndex) {
// 9.1)
return value.Value, nil
}
} else if value.IsValue() && (((value.Has(KeywordLanguage) && language != "" && language != KeywordNull && strings.EqualFold(value.Language, language)) || (!value.Has(KeywordLanguage) && language != "" && language == KeywordNull) || (!value.Has(KeywordLanguage) && language == "")) && ((value.Has(KeywordDirection) && direction != "" && direction != KeywordNull && strings.EqualFold(value.Direction, direction)) || (!value.Has(KeywordDirection) && direction != "" && direction == KeywordNull) || (!value.Has(KeywordDirection) && direction == ""))) {
// 10)
if !value.Has(KeywordIndex) || (value.Has(KeywordIndex) && defOK && slices.Contains(def.Container, KeywordIndex)) {
// 10.1)
return value.Value, nil
}
}
// 11) doesn't seem necessary
return nil, nil
}
func (p *Processor) Compact(
compactionCtx json.RawMessage,
document []Node,
documentURL string,
) (json.RawMessage, error) {
ctx, err := p.Context(compactionCtx, documentURL)
if err != nil {
return nil, err
}
if len(document) == 0 {
return json.RawMessage(`{}`), nil
}
if ctx == nil {
return json.Marshal(document)
}
res, err := p.compact(
ctx,
"",
document,
p.compactArrays,
p.ordered,
)
if err != nil {
return nil, err
}
if res == nil {
return json.RawMessage(`{}`), nil
}
if v, isObject := res.(map[string]any); isObject && p.compactArrays {
if len(compactionCtx) > 2 {
v[KeywordContext] = compactionCtx
}
return json.Marshal(v)
}
alias, err := p.compactIRI(ctx, KeywordGraph, nil, true, false)
if err != nil {
return nil, err
}
result := map[string]any{
alias: res,
}
if len(compactionCtx) > 2 {
result[KeywordContext] = compactionCtx
}
return json.Marshal(result)
}
func (p *Processor) compact(
activeContext *Context,
activeProperty string,
element any,
compactArrays bool,
ordered bool,
) (any, error) {
var activeTermDefinition Term
if activeProperty != "" {
activeTermDefinition = activeContext.defs[activeProperty]
}
// 1)
typeScopedContext := activeContext
// 2)
elemArray, isArray := element.([]Node)
object, isObject := element.(Node)
if !isArray && !isObject {
return element, nil
}
// 3)
if isArray {
// 3.1)
result := make([]any, 0, len(elemArray))
// 3.2)
for _, elem := range elemArray {
// 3.2.1)
compactedItem, err := p.compact(activeContext, activeProperty, elem, compactArrays, ordered)
if err != nil {
return nil, err
}
// 3.2.2)
if compactedItem != nil {
result = append(result, compactedItem)
}
}
// 3.3)
if len(result) != 1 || !compactArrays || activeProperty == KeywordGraph || activeProperty == KeywordSet {
return result, nil
}
asList := slices.Contains(activeTermDefinition.Container, KeywordList)
asSet := slices.Contains(activeTermDefinition.Container, KeywordSet)
if asList || asSet {
return result, nil
}
// 3.4)
return result[0], nil
}
// 4)
if !isObject {
return fmt.Errorf("what the fuck"), nil
}
// 5)
if activeContext.previousContext != nil {
if !object.Has(KeywordValue) && (!object.Has(KeywordID) || len(object.PropertySet()) > 1) {
activeContext = activeContext.previousContext
}
}
// 6)
if activeTermDefinition.Context != nil {
opts := newCtxProcessingOpts()
opts.override = true
nctx, err := p.context(activeContext, activeTermDefinition.Context, activeTermDefinition.BaseIRI, opts)
if err != nil {
return nil, err
}
activeContext = nctx
activeTermDefinition = activeContext.defs[activeProperty]
}
// 7)
if object.Has(KeywordValue) || object.Has(KeywordID) {
if activeTermDefinition.Type == KeywordJSON {
return object.Value, nil
}
value, err := p.compactValue(activeContext, activeProperty, &object)
if err != nil {
return nil, err
}
if value != nil {
return value, nil
}
}
// 8)
if object.IsList() &&
slices.Contains(activeTermDefinition.Container, KeywordList) {
return p.compact(
activeContext,
activeProperty,
object.List,
compactArrays,
ordered,
)
}
// 9)
insideReverse := activeProperty == KeywordReverse
// 10)
result := map[string]any{}
// 11)
if object.Has(KeywordType) {
compactedTypes := make([]string, 0, len(object.Type))
for _, t := range object.Type {
res, err := p.compactIRI(activeContext, t, nil, true, false)
if err != nil {
return nil, err
}
compactedTypes = append(compactedTypes, res)
}
slices.Sort(compactedTypes)
// 11.1)
for _, t := range compactedTypes {
if cdef, cok := typeScopedContext.defs[t]; cok && cdef.Context != nil {
opts := newCtxProcessingOpts()
opts.propagate = false
nctx, err := p.context(
activeContext,
cdef.Context,
cdef.BaseIRI,
opts,
)
if err != nil {
return nil, err
}
activeContext = nctx
}
}
}
// 12)
expandedProperties := slices.Collect(maps.Keys(object.PropertySet()))
if ordered {
slices.Sort(expandedProperties)
}
for _, expandedProperty := range expandedProperties {
// 12.1)
if expandedProperty == KeywordID {
// 12.1.1)
cv, err := p.compactIRI(activeContext, object.ID, nil, false, false)
if err != nil {
return nil, err
}
// 12.1.2)
alias, err := p.compactIRI(activeContext, KeywordID, nil, true, false)
if err != nil {
return nil, err
}
// 12.1.3)
result[alias] = cv
continue
}
if expandedProperty == KeywordType {
// 12.2.1) 12.2.2)
vt := make([]string, 0, len(object.Type))
for _, t := range object.Type {
res, err := p.compactIRI(typeScopedContext, t, nil, true, false)
if err != nil {
return nil, err
}
vt = append(vt, res)
}
// 12.2.3)
alias, err := p.compactIRI(activeContext, KeywordType, nil, true, false)
if err != nil {
return nil, err
}
// 12.2.4)
asArray := !compactArrays
if tdef, tok := activeContext.defs[alias]; tok && slices.Contains(tdef.Container, KeywordSet) && !p.modeLD10 {
asArray = true
}
// 12.2.5)
if asArray || len(vt) > 1 {
result[alias] = vt
} else {
result[alias] = vt[0]
}
// 12.2.6)
continue
}
// 12.3)
if expandedProperty == KeywordReverse {
// 12.3.1)
res := make([]any, 0, len(object.Reverse))
for k, elem := range object.Reverse {
compactedValue, err := p.compact(
activeContext,
KeywordReverse,
Node{Properties: Properties{
k: elem,
}},
compactArrays,
ordered,
)
if err != nil {
return nil, err
}
obj, objOK := compactedValue.(map[string]any)
// 12.3.2)
if objOK {
for prop, val := range obj {
if rdef, rok := activeContext.defs[prop]; rok && rdef.Reverse {
asArray := !compactArrays
if slices.Contains(rdef.Container, KeywordSet) {
asArray = true
}
valArray, valIsArray := val.([]any)
if asArray {
if valIsArray {
result[prop] = valArray
} else {
result[prop] = []any{val}
}
} else {
result[prop] = val
}
delete(obj, prop)
}
}
if len(obj) != 0 {
res = append(res, obj)
}
}
}
if len(res) == 0 {
continue
}
final := res[0].(map[string]any)
for _, elem := range res[1:] {
maps.Copy(final, elem.(map[string]any))
}
// 12.3.3)
alias, err := p.compactIRI(activeContext, KeywordReverse, nil, true, false)
if err != nil {
return nil, err
}
result[alias] = final
// 12.3.4)
continue
}
// 12.4)
if expandedProperty == KeywordPreserve {
return nil, ErrPreserveUnsupported
}
// 12.5)
if slices.Contains(activeTermDefinition.Container, KeywordIndex) && expandedProperty == KeywordIndex {
continue
} else if expandedProperty == KeywordDirection ||
expandedProperty == KeywordIndex ||
expandedProperty == KeywordLanguage ||
expandedProperty == KeywordValue {
// 12.6)
// 12.6.1)
alias, err := p.compactIRI(activeContext, expandedProperty, nil, true, false)
if err != nil {
return nil, err
}
// 12.6.2)
var value any
switch expandedProperty {
case KeywordDirection:
value = object.Direction
case KeywordIndex:
value = object.Index
case KeywordLanguage:
value = object.Language
case KeywordValue:
value = object.Value
}
result[alias] = value
continue
}
var expandedValue []Node
if expandedProperty == KeywordList {
expandedValue = object.List
} else if expandedProperty == KeywordGraph {
expandedValue = object.Graph
} else if expandedProperty == KeywordIncluded {
expandedValue = object.Included
} else {
expandedValue = object.Properties[expandedProperty]
}
// 12.7
if len(expandedValue) == 0 {
itemActiveProperty, err := p.compactIRI(
activeContext,
expandedProperty,
expandedValue,
true, insideReverse,
)
if err != nil {
return nil, err
}
var nestResult map[string]any
if edef, eok := activeContext.defs[itemActiveProperty]; eok && edef.Nest != "" {
// 12.7.2)
term, err := p.expandIRI(activeContext, edef.Nest, false, true, nil, nil)
if err != nil {
return nil, err
}
// 12.7.2.1)
if term != KeywordNest {
return nil, ErrInvalidNestValue
}
term = edef.Nest
// 12.7.2.2)
if _, ok := result[term]; !ok {
result[term] = map[string]any{}
}
// 12.7.2.3)
nestResult = result[term].(map[string]any)
} else {
// 12.7.3)
nestResult = result
}
// 12.7.4)
nestResult[itemActiveProperty] = []any{}
}
// 12.8)
for _, expandedItem := range expandedValue {
// 12.8.1)
itemActiveProperty, err := p.compactIRI(
activeContext,
expandedProperty,
expandedItem,
true, insideReverse,
)
if err != nil {
return nil, err
}
// 12.8.2)
var nestResult map[string]any
if edef, eok := activeContext.defs[itemActiveProperty]; eok && edef.Nest != "" {
// 12.8.2.1)
term, err := p.expandIRI(activeContext, edef.Nest, false, true, nil, nil)
if err != nil {
return nil, err
}
if term != KeywordNest {
return nil, ErrInvalidNestValue
}
term = edef.Nest
// 12.8.2.2)
if _, ok := result[term]; !ok {
result[term] = map[string]any{}
}
// 12.8.2.3)
nestResult = result[term].(map[string]any)
} else {
// 12.8.3)
nestResult = result
}
itemDef := activeContext.defs[itemActiveProperty]
// 12.8.4)
container := itemDef.Container
// 12.8.5)
asArray := !compactArrays
if itemActiveProperty == KeywordList || itemActiveProperty == KeywordGraph || slices.Contains(container, KeywordSet) {
asArray = true
}
// 12.8.6)
var itemToCompact any
if expandedItem.IsList() {
itemToCompact = expandedItem.List
} else if expandedItem.IsGraph() {
itemToCompact = expandedItem.Graph
} else {
itemToCompact = expandedItem
}
compactedItem, err := p.compact(
activeContext,
itemActiveProperty,
itemToCompact,
compactArrays,
ordered,
)
if err != nil {
return nil, err
}
// 12.8.7)
if expandedItem.IsList() {
_, isArray := compactedItem.([]any)
// 12.8.7.1)
if !isArray {
compactedItem = []any{compactedItem}
}
// 12.8.7.2)
if !slices.Contains(container, KeywordList) {
// 12.8.7.2.1)
compactedMap := map[string]any{}
alias, err := p.compactIRI(
activeContext,
KeywordList,
nil, true, false,
)
if err != nil {
return nil, err
}
compactedMap[alias] = compactedItem
// 12.8.7.2.2)
if expandedItem.Has(KeywordIndex) {
iAlias, err := p.compactIRI(
activeContext,
KeywordIndex,
nil, true, false,
)
if err != nil {
return nil, err
}
compactedMap[iAlias] = expandedItem.Index
}
// 12.8.7.2.3)
if v, ok := nestResult[itemActiveProperty]; ok {
vlist, vok := v.([]any)
if !vok {
vlist = []any(vlist)
}
vlist = append(vlist, compactedMap)
nestResult[itemActiveProperty] = vlist
} else {
if asArray {
nestResult[itemActiveProperty] = []any{compactedMap}
} else {
nestResult[itemActiveProperty] = compactedMap
}
}
} else {
// 12.8.7.3)
nestResult[itemActiveProperty] = compactedItem
}
} else if expandedItem.IsGraph() {
// 12.8.8)
if slices.Contains(container, KeywordGraph) &&
slices.Contains(container, KeywordID) {
// 12.8.8.1)
mapObject, ok := nestResult[itemActiveProperty].(map[string]any)
if !ok {
// 12.8.8.1.1)
mapObject = map[string]any{}
}
// 12.8.8.1.2)
vocab := true
key := cmp.Or(expandedItem.ID, KeywordNone)
if expandedItem.Has(KeywordID) {
vocab = false
}
alias, err := p.compactIRI(activeContext, key, nil, vocab, false)
if err != nil {
return nil, err
}
// 12.8.8.1.3)
if v, ok := mapObject[alias]; ok {
vlist, vok := v.([]any)
if !vok {
vlist = []any{v}
}
vlist = append(vlist, compactedItem)
mapObject[alias] = vlist
} else {
if asArray {
if _, isArray := compactedItem.([]any); isArray {
mapObject[alias] = compactedItem
} else {
mapObject[alias] = []any{compactedItem}
}
} else {
mapObject[alias] = compactedItem
}
}
nestResult[itemActiveProperty] = mapObject
} else if slices.Contains(container, KeywordGraph) &&
slices.Contains(container, KeywordIndex) && expandedItem.IsSimpleGraph() {
// 12.8.8.2)
mapObject, ok := nestResult[itemActiveProperty].(map[string]any)
if !ok {
// 12.8.8.2.1)
mapObject = map[string]any{}
}
// 12.8.8.2.2)
key := cmp.Or(expandedItem.Index, KeywordNone)
// 12.8.8.2.3)
if v, ok := mapObject[key]; ok {
vlist, vok := v.([]any)
if !vok {
vlist = []any{v}
}
vlist = append(vlist, compactedItem)
mapObject[key] = vlist
} else {
if asArray {
if _, isArray := compactedItem.([]any); isArray {
mapObject[key] = compactedItem
} else {
mapObject[key] = []any{compactedItem}
}
} else {
mapObject[key] = compactedItem
}
}
nestResult[itemActiveProperty] = mapObject
} else if slices.Contains(container, KeywordGraph) && expandedItem.IsSimpleGraph() {
// 12.8.8.3)
clist, cok := compactedItem.([]any)
// 12.8.8.3.1)
if cok && len(clist) > 1 {
alias, err := p.compactIRI(activeContext, KeywordIncluded, nil, true, false)
if err != nil {
return nil, err
}
compactedItem = map[string]any{
alias: compactedItem,
}
}
// 12.8.8.3.2)
if v, ok := nestResult[itemActiveProperty]; ok {
vlist, vok := v.([]any)
if !vok {
vlist = []any{v}
}
if cok {
vlist = append(vlist, clist...)
} else {
vlist = append(vlist, compactedItem)
}
nestResult[itemActiveProperty] = vlist
} else {
_, ncok := compactedItem.([]any)
if asArray && !ncok {
nestResult[itemActiveProperty] = []any{compactedItem}
} else {
nestResult[itemActiveProperty] = compactedItem
}
}
} else {
// 12.8.8.4)
alias, err := p.compactIRI(activeContext, KeywordGraph, nil, true, false)
if err != nil {
return nil, err
}
// 12.8.8.4.1)
compactedItem := map[string]any{
alias: compactedItem,
}
// 12.8.8.4.2)
if expandedItem.Has(KeywordID) {
alias, err := p.compactIRI(activeContext, KeywordID, nil, true, false)
if err != nil {
return nil, err
}
val, err := p.compactIRI(activeContext, expandedItem.ID, nil, false, false)
if err != nil {
return nil, err
}
compactedItem[alias] = val
}
// 12.8.8.4.3)
if expandedItem.Has(KeywordIndex) {
alias, err := p.compactIRI(activeContext, KeywordIndex, nil, true, false)
if err != nil {
return nil, err
}
compactedItem[alias] = expandedItem.Index
}
// 12.8.8.4.4)
if v, ok := nestResult[itemActiveProperty]; ok {
vlist, vok := v.([]any)
if !vok {
vlist = []any{v}
}
vlist = append(vlist, compactedItem)
nestResult[itemActiveProperty] = vlist
} else {
if asArray {
nestResult[itemActiveProperty] = []any{compactedItem}
} else {
nestResult[itemActiveProperty] = compactedItem
}
}
}
} else if !slices.Contains(container, KeywordGraph) && (slices.Contains(container, KeywordLanguage) ||
slices.Contains(container, KeywordIndex) ||
slices.Contains(container, KeywordID) ||
slices.Contains(container, KeywordType)) {
// 12.8.9)
mapObject, ok := nestResult[itemActiveProperty].(map[string]any)
if !ok {
// 12.8.9.1)
mapObject = map[string]any{}
}
key := KeywordNull // this is invalid so we'll immediate see bugs
if slices.Contains(container, KeywordLanguage) {
key = KeywordLanguage
} else if slices.Contains(container, KeywordIndex) {
key = KeywordIndex
} else if slices.Contains(container, KeywordID) {
key = KeywordID
} else if slices.Contains(container, KeywordType) {
key = KeywordType
}
// 12.8.9.2)
containerKey, err := p.compactIRI(activeContext,
key, nil, true, false)
if err != nil {
return nil, err
}
// 12.8.9.3)
indexKey := KeywordIndex
if idef, iok := activeContext.defs[itemActiveProperty]; iok && idef.Index != "" {
indexKey = idef.Index
}
mapKey := ""
// 12.8.9.4)
if expandedItem.IsValue() && slices.Contains(container, KeywordLanguage) {
compactedItem = expandedItem.Value
if expandedItem.Has(KeywordLanguage) {
mapKey = expandedItem.Language
}
} else if slices.Contains(container, KeywordIndex) && indexKey == KeywordIndex {
// 12.8.9.5)
if expandedItem.Has(KeywordIndex) {
mapKey = expandedItem.Index
}
} else if slices.Contains(container, KeywordIndex) && indexKey != KeywordIndex {
// 12.8.9.6)
// 12.8.9.6.1)
expIdx, err := p.expandIRI(activeContext, indexKey, false, false, nil, nil)
if err != nil {
return nil, err
}
containerKey, err = p.compactIRI(activeContext, expIdx, nil, true, false)
if err != nil {
return nil, err
}
// 12.8.9.6.2)
if compactedObject, isObject := compactedItem.(map[string]any); isObject {
if value, vok := compactedObject[containerKey]; vok {
if nv, nok := value.(json.RawMessage); nok {
var m string
if err := json.Unmarshal(nv, &m); err != nil {
return nil, err
}
mapKey = m
delete(compactedObject, containerKey)
}
if nv, nok := value.(string); nok {
mapKey = nv
delete(compactedObject, containerKey)
}
if nv, nok := value.([]any); nok {
if v, vok := nv[0].(json.RawMessage); vok {
var m string
if err := json.Unmarshal(v, &m); err != nil {
return nil, err
}
mapKey = m
}
if v, vok := nv[0].(string); vok {
mapKey = v
}
lnv := len(nv)
if lnv == 2 {
compactedObject[containerKey] = nv[1]
} else if lnv > 2 {
compactedObject[containerKey] = nv[1:]
} else {
delete(compactedObject, containerKey)
}
}
}
compactedItem = compactedObject
}
} else if slices.Contains(container, KeywordID) {
// 12.8.9.7)
if compactedObject, ok := compactedItem.(map[string]any); ok {
if value, vok := compactedObject[containerKey]; vok {
mapKey = value.(string)
delete(compactedObject, containerKey)
}
compactedItem = compactedObject
}
} else if slices.Contains(container, KeywordType) {
// 12.8.9.8)
if compactedObject, isObject := compactedItem.(map[string]any); isObject {
// 12.8.9.8.1)
if value, vok := compactedObject[containerKey]; vok {
if vlist, lok := value.([]string); lok {
mapKey = vlist[0]
if len(vlist) == 2 {
compactedObject[containerKey] = vlist[1]
} else if len(vlist) > 2 {
compactedObject[containerKey] = vlist[1:]
}
}
if s, sok := value.(string); sok {
mapKey = s
// 12.8.9.8.2)
delete(compactedObject, containerKey)
}
}
// 12.8.9.8.4)
if len(compactedObject) == 1 {
for k := range compactedObject {
expIri, err := p.expandIRI(activeContext, k, false, true, nil, nil)
if err != nil {
return nil, err
}
if expIri == KeywordID {
res, err := p.compact(
activeContext,
itemActiveProperty,
Node{ID: expandedItem.ID},
false, false,
)
if err != nil {
return nil, err
}
compactedItem = res
}
}
} else {
compactedItem = compactedObject
}
}
}
// 12.8.9.9
if mapKey == "" {
alias, err := p.compactIRI(activeContext, KeywordNone, nil, true, false)
if err != nil {
return nil, err
}
mapKey = alias
}
// 12.8.9.10)
if v, ok := mapObject[mapKey]; ok {
vlist, ok := v.([]any)
if !ok {
vlist = []any{v}
}
vlist = append(vlist, compactedItem)
mapObject[mapKey] = vlist
} else {
if asArray {
mapObject[mapKey] = []any{compactedItem}
} else {
mapObject[mapKey] = compactedItem
}
}
nestResult[itemActiveProperty] = mapObject
} else {
// 12.8.10)
if v, ok := nestResult[itemActiveProperty]; ok {
vlist, ok := v.([]any)
if !ok {
vlist = []any{v}
}
vlist = append(vlist, compactedItem)
nestResult[itemActiveProperty] = vlist
} else {
if asArray {
if itemDef.Type == KeywordJSON && json.IsArray(expandedItem.Value) {
nestResult[itemActiveProperty] = compactedItem
} else {
nestResult[itemActiveProperty] = []any{compactedItem}
}
} else {
nestResult[itemActiveProperty] = compactedItem
}
}
}
}
}
return result, nil
}
// sortedLeast sorts strings based on smallest first and if they're
// equal, then by string comparison.
func sortedLeast(a, b string) int {
if len(a) < len(b) {
return -1
}
if len(a) > len(b) {
return 1
}
return strings.Compare(a, b)
}