185 行
4.8 KiB
Go
185 行
4.8 KiB
Go
|
package rdf
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
ALIAS_DELIMITER = ":"
|
||
|
ID = "@id"
|
||
|
)
|
||
|
|
||
|
// Ontology returns different RDF "actions" or "handlers" that are able to
|
||
|
// interpret the schema definitions as actions upon a set of data, specific
|
||
|
// for this ontology.
|
||
|
type Ontology interface {
|
||
|
// String representation of this ontology.
|
||
|
String() string
|
||
|
// Load loads the entire ontology.
|
||
|
Load() ([]RDFNode, error)
|
||
|
// Load loads the entire ontology with a specific alias.
|
||
|
LoadAsAlias(s string) ([]RDFNode, error)
|
||
|
// LoadElement loads a specific element of the ontology by name. The
|
||
|
// payload may be nil.
|
||
|
LoadElement(name string, payload map[string]interface{}) ([]RDFNode, error)
|
||
|
}
|
||
|
|
||
|
// aliasedNode represents a context element that has a special reserved alias.
|
||
|
type aliasedNode struct {
|
||
|
Alias string
|
||
|
Nodes []RDFNode
|
||
|
}
|
||
|
|
||
|
// RDFRegistry implements RDFGetter and manages the different ontologies needed
|
||
|
// to determine the generated Go code.
|
||
|
type RDFRegistry struct {
|
||
|
ontologies map[string]Ontology
|
||
|
aliases map[string]string
|
||
|
aliasedNodes map[string]aliasedNode
|
||
|
mu sync.Mutex
|
||
|
}
|
||
|
|
||
|
// setAlias sets an alias for a string.
|
||
|
func (r *RDFRegistry) setAlias(alias, s string) error {
|
||
|
if _, ok := r.aliases[alias]; ok {
|
||
|
return fmt.Errorf("already have alias for %s", alias)
|
||
|
}
|
||
|
r.aliases[alias] = s
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// setAliasedNode sets an alias for a node.
|
||
|
func (r *RDFRegistry) setAliasedNode(alias string, nodes []RDFNode) error {
|
||
|
if _, ok := r.aliasedNodes[alias]; ok {
|
||
|
return fmt.Errorf("already have aliased node for %s", alias)
|
||
|
}
|
||
|
r.aliasedNodes[alias] = aliasedNode{
|
||
|
Alias: alias,
|
||
|
Nodes: nodes,
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// getOngology resolves an alias to a particular Ontology.
|
||
|
func (r *RDFRegistry) getOntology(alias string) (Ontology, error) {
|
||
|
if ontologyName, ok := r.aliases[alias]; !ok {
|
||
|
return nil, fmt.Errorf("missing alias %q", alias)
|
||
|
} else if ontology, ok := r.ontologies[ontologyName]; !ok {
|
||
|
return nil, fmt.Errorf("alias %q resolved but missing ontology with name %q", alias, ontologyName)
|
||
|
} else {
|
||
|
return ontology, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// loadElement will handle the aliasing of an ontology and retrieve the nodes
|
||
|
// required for a specific element within that ontology.
|
||
|
func (r *RDFRegistry) loadElement(alias, element string, payload map[string]interface{}) (n []RDFNode, e error) {
|
||
|
if ontName, ok := r.aliases[alias]; !ok {
|
||
|
e = fmt.Errorf("no alias to ontology for %s", alias)
|
||
|
return
|
||
|
} else if ontology, ok := r.ontologies[ontName]; !ok {
|
||
|
e = fmt.Errorf("no ontology named %s for alias %s", ontName, alias)
|
||
|
return
|
||
|
} else {
|
||
|
n, e = ontology.LoadElement(element, payload)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AddOntology adds an RDF ontology to the registry.
|
||
|
func (r *RDFRegistry) AddOntology(s string, o Ontology) error {
|
||
|
r.mu.Lock()
|
||
|
defer r.mu.Unlock()
|
||
|
if r.ontologies == nil {
|
||
|
r.ontologies = make(map[string]Ontology, 1)
|
||
|
}
|
||
|
if _, ok := r.ontologies[s]; ok {
|
||
|
return fmt.Errorf("ontology already registered for %q", s)
|
||
|
}
|
||
|
r.ontologies[s] = o
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetFor gets RDFKeyers and RDFValuers based on a context's string.
|
||
|
//
|
||
|
// Implements RDFGetter.
|
||
|
func (r *RDFRegistry) GetFor(s string) (n []RDFNode, e error) {
|
||
|
r.mu.Lock()
|
||
|
defer r.mu.Unlock()
|
||
|
ontology, ok := r.ontologies[s]
|
||
|
if !ok {
|
||
|
e = fmt.Errorf("no ontology for %s", s)
|
||
|
return
|
||
|
}
|
||
|
return ontology.Load()
|
||
|
}
|
||
|
|
||
|
// GetAliased gets RDFKeyers and RDFValuers based on a context string and its
|
||
|
// alias.
|
||
|
//
|
||
|
// Implements RDFGetter.
|
||
|
func (r *RDFRegistry) GetAliased(alias, s string) (n []RDFNode, e error) {
|
||
|
r.mu.Lock()
|
||
|
defer r.mu.Unlock()
|
||
|
strs := strings.Split(s, ALIAS_DELIMITER)
|
||
|
if len(strs) == 1 {
|
||
|
if e = r.setAlias(alias, s); e != nil {
|
||
|
return
|
||
|
}
|
||
|
return r.GetFor(s)
|
||
|
} else if len(strs) == 2 {
|
||
|
var o Ontology
|
||
|
o, e = r.getOntology(strs[0])
|
||
|
if e != nil {
|
||
|
return
|
||
|
}
|
||
|
n, e = o.LoadElement(strs[1], nil)
|
||
|
return
|
||
|
} else {
|
||
|
e = fmt.Errorf("too many delimiters in %s", s)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetAliasedObject gets RDFKeyers and RDFValuers based on a context object and
|
||
|
// its alias and definition.
|
||
|
//
|
||
|
// Implements RDFGetter.
|
||
|
func (r *RDFRegistry) GetAliasedObject(alias string, object map[string]interface{}) (n []RDFNode, e error) {
|
||
|
r.mu.Lock()
|
||
|
defer r.mu.Unlock()
|
||
|
var iObj interface{} = object
|
||
|
if obj, ok := iObj.(map[string]string); !ok {
|
||
|
e = fmt.Errorf("object in GetAliasedObject must be a map[string]string")
|
||
|
return
|
||
|
} else {
|
||
|
element, ok := obj[ID]
|
||
|
if !ok {
|
||
|
e = fmt.Errorf("aliased object does not have %s value", ID)
|
||
|
return
|
||
|
}
|
||
|
var nodes []RDFNode
|
||
|
strs := strings.Split(element, ALIAS_DELIMITER)
|
||
|
if len(strs) == 1 {
|
||
|
n, e = r.GetFor(strs[0])
|
||
|
} else if len(strs) == 2 {
|
||
|
var o Ontology
|
||
|
o, e = r.getOntology(strs[0])
|
||
|
if e != nil {
|
||
|
return
|
||
|
}
|
||
|
n, e = o.LoadElement(strs[1], object)
|
||
|
return
|
||
|
}
|
||
|
if e != nil {
|
||
|
return
|
||
|
}
|
||
|
if e = r.setAliasedNode(alias, nodes); e != nil {
|
||
|
return
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
}
|