diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBuiltIn.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBuiltIn.java index 61a09d2a511..393022c33be 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBuiltIn.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBuiltIn.java @@ -33,7 +33,7 @@ public static class Item implements AxiomItemDefinition { public static final AxiomItemDefinition VERSION = new Item("version", Type.STRING, true); public static final AxiomItemDefinition TYPE_REFERENCE = new Item("type", Type.TYPE_REFERENCE, true); public static final AxiomItemDefinition TYPE_DEFINITION = new Item("type", Type.TYPE_DEFINITION, false); - public static final AxiomItemDefinition SUPERTYPE_REFERENCE = new Item("extends", Type.IDENTIFIER, false); + public static final AxiomItemDefinition SUPERTYPE_REFERENCE = new Item("extends", Type.TYPE_REFERENCE, false); public static final AxiomItemDefinition ROOT_DEFINITION = new Item("root", Type.ITEM_DEFINITION, false); public static final AxiomItemDefinition OBJECT_DEFINITION = new Item("object", Type.OBJECT_DEFINITION, false); public static final AxiomItemDefinition REFERENCE_DEFINITION = new Item("reference", Type.ITEM_DEFINITION, false); diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomSchemaContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomSchemaContext.java index 36c260f1611..89e8565aa38 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomSchemaContext.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomSchemaContext.java @@ -9,6 +9,8 @@ public interface AxiomSchemaContext { Collection roots(); + Optional getRoot(AxiomIdentifier type); + Optional getType(AxiomIdentifier type); Collection types(); diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSchemaContextImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSchemaContextImpl.java new file mode 100644 index 00000000000..e71b827bc42 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSchemaContextImpl.java @@ -0,0 +1,58 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.lang.api.AxiomBuiltIn; +import com.evolveum.axiom.lang.api.AxiomItemDefinition; +import com.evolveum.axiom.lang.api.AxiomSchemaContext; +import com.evolveum.axiom.lang.api.AxiomTypeDefinition; +import com.google.common.collect.ImmutableMap; + +public class AxiomSchemaContextImpl implements AxiomSchemaContext { + + private Map roots; + private Map types; + + public AxiomSchemaContextImpl(Map roots, + Map types) { + this.roots = roots; + this.types = types; + } + + public static AxiomSchemaContext of(Map roots, + Map types) { + return new AxiomSchemaContextImpl(roots, types); + } + + @Override + public Collection roots() { + return roots.values(); + } + + @Override + public Optional getType(AxiomIdentifier type) { + return Optional.ofNullable(types.get(type)); + } + + @Override + public Collection types() { + return types.values(); + } + + @Override + public Optional getRoot(AxiomIdentifier type) { + return Optional.ofNullable(roots.get(type)); + } + + public static AxiomSchemaContextImpl boostrapContext() { + return new AxiomSchemaContextImpl(ImmutableMap.of(AxiomBuiltIn.Item.MODEL_DEFINITION.name(), AxiomBuiltIn.Item.MODEL_DEFINITION), + ImmutableMap.of()); + } + + public static AxiomSchemaContext baseLanguageContext() { + return ModelReactorContext.BASE_LANGUAGE.get(); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomTypeDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomTypeDefinitionImpl.java index 69fe6eb9c5b..59f9b912a09 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomTypeDefinitionImpl.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomTypeDefinitionImpl.java @@ -22,6 +22,8 @@ public class AxiomTypeDefinitionImpl extends AbstractAxiomBaseDefinition impleme public static final Factory FACTORY =AxiomTypeDefinitionImpl::new; private final Map items; + private final Optional superType; + private Optional argument; public AxiomTypeDefinitionImpl(AxiomIdentifier keyword, AxiomIdentifier value, List> children, Multimap> keywordMap) { @@ -29,16 +31,22 @@ public AxiomTypeDefinitionImpl(AxiomIdentifier keyword, AxiomIdentifier value, L ImmutableMap.Builder builder = ImmutableMap.builder(); putAll(builder, children(ITEM_DEFINITION.name(), AxiomItemDefinition.class)); items = builder.build(); + superType = first(SUPERTYPE_REFERENCE.name(), AxiomTypeDefinition.class); + argument = firstValue(ARGUMENT.name(), AxiomIdentifier.class) + .flatMap((AxiomIdentifier k) -> item(k)); } @Override public Optional argument() { - return first(ARGUMENT.name(), AxiomItemDefinition.class); + if(argument.isEmpty() && superType().isPresent()) { + return superType().get().argument(); + } + return argument; } @Override public Optional superType() { - return first(SUPERTYPE_REFERENCE.name(), AxiomTypeDefinition.class); + return superType; } @Override diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/BasicStatementRule.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/BasicStatementRule.java index f3bc03da3a7..7e583efff35 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/BasicStatementRule.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/BasicStatementRule.java @@ -5,7 +5,6 @@ import java.util.function.Supplier; import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomBuiltIn; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Type; import com.evolveum.axiom.lang.api.AxiomItemDefinition; @@ -13,7 +12,6 @@ import com.evolveum.axiom.lang.api.stmt.AxiomStatement; import com.google.common.collect.ImmutableSet; -import static com.evolveum.axiom.lang.api.AxiomBuiltIn.*; public enum BasicStatementRule implements StatementRule { @@ -36,27 +34,27 @@ public void apply(StatementRuleContext rule) throws AxiomSemant } }, - ADD_TYPE_TO_ITEM(items(Item.TYPE_REFERENCE), types(Type.TYPE_REFERENCE)) { + EXPAND_TYPE_REFERENCE(all(), types(Type.TYPE_REFERENCE)) { @Override public void apply(StatementRuleContext rule) throws AxiomSemanticException { AxiomIdentifier type = rule.requireValue(); - Requirement>> typeDef = rule.requireGlobalItem(Item.TYPE_DEFINITION, type); + Requirement> typeDef = rule.requireGlobalItem(Item.TYPE_DEFINITION, type); rule.apply(ctx -> { - ctx.parent().builder().add(Item.TYPE_DEFINITION, typeDef.get()); + ctx.replace(typeDef); }); rule.errorMessage(() -> rule.error("type '%s' was not found.", type)); } - }, - + }; +/* ADD_SUPERTYPE(items(), types(Type.TYPE_DEFINITION)) { @Override public void apply(StatementRuleContext rule) throws AxiomSemanticException { Optional superType = rule.optionalChildValue(Item.SUPERTYPE_REFERENCE, AxiomIdentifier.class); if(superType.isPresent()) { - Requirement>> req = rule.requireGlobalItem(Item.TYPE_DEFINITION, superType.get()); + Requirement> req = rule.requireGlobalItem(Item.TYPE_DEFINITION, superType.get()); rule.apply((ctx) -> { - ctx.builder().add(Item.SUPERTYPE_REFERENCE, req.get()); + //ctx.builder().add(Item.SUPERTYPE_REFERENCE, req.get()); }); rule.errorMessage(() -> { if(!req.isSatisfied()) { @@ -66,7 +64,7 @@ public void apply(StatementRuleContext rule) throws AxiomSemant }); } } - }; + };*/ private final Set items; private final Set types; diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Deffered.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Deffered.java new file mode 100644 index 00000000000..2e6bfdf74b6 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Deffered.java @@ -0,0 +1,40 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Optional; + +import com.google.common.base.Preconditions; + +class Deffered extends Requirement.Delegated { + + private Object ret; + + Deffered(Requirement delegate) { + ret = delegate; + } + + @Override + Requirement delegate() { + if(ret instanceof Requirement) { + return (Requirement) ret; + } + return null; + } + + @Override + public boolean isSatisfied() { + if(delegate() != null) { + if(delegate().isSatisfied()) { + ret = delegate().get(); + } else { + return false; + } + } + return true; + } + + @Override + public T get() { + Preconditions.checkState(isSatisfied(), "Requirement was not satisfied"); + return (T) ret; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DelegatedRequirement.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DelegatedRequirement.java new file mode 100644 index 00000000000..f83c3ddd823 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DelegatedRequirement.java @@ -0,0 +1,7 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Optional; + +import com.google.common.base.Preconditions; + + diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ModelReactorContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ModelReactorContext.java index fc9dccdd0c8..ff43618ae1e 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ModelReactorContext.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ModelReactorContext.java @@ -1,6 +1,9 @@ package com.evolveum.axiom.lang.impl; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -8,18 +11,22 @@ import java.util.Optional; import java.util.function.Supplier; +import javax.management.RuntimeErrorException; + import org.jetbrains.annotations.NotNull; import com.evolveum.axiom.api.AxiomIdentifier; import com.evolveum.axiom.concepts.Lazy; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Type; +import com.evolveum.axiom.lang.api.AxiomBuiltIn; import com.evolveum.axiom.lang.api.AxiomItemDefinition; +import com.evolveum.axiom.lang.api.AxiomSchemaContext; import com.evolveum.axiom.lang.api.AxiomTypeDefinition; import com.evolveum.axiom.lang.api.stmt.AxiomStatement; import com.evolveum.axiom.lang.api.stmt.SourceLocation; import com.evolveum.axiom.lang.impl.AxiomStatementImpl.Factory; -import com.evolveum.axiom.lang.impl.StatementContextImpl.RuleContextImpl; +import com.google.common.collect.ImmutableMap; public class ModelReactorContext implements AxiomIdentifierResolver { @@ -27,29 +34,76 @@ public class ModelReactorContext implements AxiomIdentifierResolver { private static final AxiomIdentifier ROOT = AxiomIdentifier.from("root","root"); + private static final String AXIOM_LANG_RESOURCE = "/axiom-lang.axiom"; + + + private static final Lazy BASE_LANGUAGE_SOURCE = Lazy.from(() -> { + InputStream stream = AxiomBuiltIn.class.getResourceAsStream(AXIOM_LANG_RESOURCE); + try { + return AxiomStatementSource.from(AXIOM_LANG_RESOURCE, stream); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + + public static final Lazy BASE_LANGUAGE = Lazy.from(() -> { + ModelReactorContext reactor = boostrapReactor(); + reactor.loadModelFromSource(BASE_LANGUAGE_SOURCE.get()); + return reactor.computeSchemaContext(); + }); + + + + public static final ModelReactorContext reactor(AxiomSchemaContext context) { + ModelReactorContext reactorContext = new ModelReactorContext(context); + defaults(reactorContext); + return reactorContext; + } + + + public static final ModelReactorContext boostrapReactor() { + ModelReactorContext reactorContext = new ModelReactorContext(AxiomSchemaContextImpl.boostrapContext()); + defaults(reactorContext); + return reactorContext; + } + + public static final ModelReactorContext defaultReactor() { + return reactor(BASE_LANGUAGE.get()); + } + + private static void defaults(ModelReactorContext reactorContext) { + reactorContext.addRules(BasicStatementRule.values()); + reactorContext.addStatementFactory(Type.TYPE_DEFINITION.name(), AxiomTypeDefinitionImpl.FACTORY); + reactorContext.addStatementFactory(Type.ITEM_DEFINITION.name(), AxiomItemDefinitionImpl.FACTORY); + } + List> rules = new ArrayList<>(); - public Map rootItems = new HashMap<>(); - Map> staticGlobalItems = new HashMap<>(); - Map> globalItems = new HashMap<>(); - Map> typeFactories = new HashMap<>(); + private final AxiomSchemaContext boostrapContext; + + public ModelReactorContext(AxiomSchemaContext boostrapContext) { + this.boostrapContext = boostrapContext; + } - List.RuleContextImpl> outstanding = new ArrayList<>(); + Map> globalItems = new HashMap<>(); + Map> typeFactories = new HashMap<>(); + List outstanding = new ArrayList<>(); List> roots = new ArrayList<>(); - public List> process() throws AxiomSemanticException { + public AxiomSchemaContext computeSchemaContext() throws AxiomSemanticException { boolean anyCompleted = false; do { anyCompleted = false; - List.RuleContextImpl> toCheck = outstanding; + List toCheck = outstanding; outstanding = new ArrayList<>(); - Iterator.RuleContextImpl> iterator = toCheck.iterator(); + Iterator iterator = toCheck.iterator(); while(iterator.hasNext()) { - StatementContextImpl.RuleContextImpl ruleCtx = iterator.next(); + StatementRuleContextImpl ruleCtx = iterator.next(); if(ruleCtx.canProcess()) { ruleCtx.perform(); iterator.remove(); @@ -65,12 +119,12 @@ public List> process() throws AxiomSemanticException { } - return buildRoots(); + return createSchemaContext(); } - private void failOutstanding(List.RuleContextImpl> report) { + private void failOutstanding(List report) { StringBuilder messages = new StringBuilder("Can not complete models, following errors occured:\n"); - for (RuleContextImpl rule : report) { + for (StatementRuleContextImpl rule : report) { RuleErrorMessage exception = rule.errorMessage(); if(exception != null) { messages.append(exception.toString()).append("\n"); @@ -81,43 +135,48 @@ private void failOutstanding(List.RuleContextImpl> repor } - private List> buildRoots() { - ArrayList> ret = new ArrayList<>(roots.size()); - for (StatementContextImpl root : roots) { - ret.add(root.build().get()); + private AxiomSchemaContext createSchemaContext() { + ImmutableMap.Builder roots = ImmutableMap.builder(); + for (StatementContextImpl rootDoc : this.roots) { + Collection rootDefs = rootDoc.asLazy().get().children(AxiomBuiltIn.Item.ROOT_DEFINITION.name(), AxiomItemDefinition.class); + for (AxiomItemDefinition rootDef : rootDefs) { + roots.put(rootDef.name(), rootDef); + } + } + ImmutableMap.Builder types = ImmutableMap.builder(); + for (StatementContextImpl root : this.globalItems.values()) { + AxiomStatement globalItem = root.asRequirement().get(); + if(globalItem instanceof AxiomTypeDefinition) { + AxiomTypeDefinition type = (AxiomTypeDefinition) globalItem; + types.put(type.name(), type); + } + } - return ret; + + return AxiomSchemaContextImpl.of(roots.build(), types.build()); + } public void registerGlobalItem(AxiomIdentifier typeName, StatementContextImpl context) { globalItems.put(typeName, context); } - public Requirement>> requireGlobalItem(AxiomIdentifier key) { - AxiomStatement maybe = staticGlobalItems.get(key); + public Requirement> requireGlobalItem(AxiomIdentifier key) { + /*AxiomStatement maybe = staticGlobalItems.get(key); if(maybe != null) { - return Requirement.immediate(Lazy.instant(maybe)); - } + return Requirement.immediate(maybe); + }*/ - return new Requirement>>() { - - @Override - boolean isSatisfied() { - StatementContextImpl maybe = globalItems.get(key); - if(maybe == null) { - return false; - } - return maybe.isCompleted(); + return (Requirement) Requirement.retriableDelegate(() -> { + StatementContextImpl maybeCtx = globalItems.get(key); + if(maybeCtx != null) { + return maybeCtx.asRequirement(); } - - @Override - public Supplier> get() { - return (Supplier) globalItems.get(key).build(); - } - }; + return null; + }); } - public void addOutstanding(StatementContextImpl.RuleContextImpl rule) { + public void addOutstanding(StatementRuleContextImpl rule) { outstanding.add(rule); } @@ -156,7 +215,7 @@ public void setValue(Object value) { @Override public Optional childDef(AxiomIdentifier statement) { - return Optional.ofNullable(rootItems.get(statement)); + return boostrapContext.getRoot(statement); } @Override @@ -177,10 +236,6 @@ public void setValue(Object value, SourceLocation loc) { } } - public void addRootItemDef(AxiomItemDefinition modelDefinition) { - rootItems.put(modelDefinition.name(), modelDefinition); - } - public void addStatementFactory(AxiomIdentifier statementType, Factory factory) { typeFactories.put(statementType, factory); } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Requirement.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Requirement.java index 631dab0e12d..06886d80661 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Requirement.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Requirement.java @@ -2,13 +2,22 @@ import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import com.evolveum.axiom.lang.api.stmt.AxiomStatement; +import com.google.common.base.Preconditions; + +public interface Requirement { -public abstract class Requirement { + static final Requirement UNSATISFIED = new Unsatified<>(); - abstract boolean isSatisfied(); - abstract public T get(); + boolean isSatisfied(); + public T get(); + + public static Requirement unsatisfied() { + return UNSATISFIED; + } public static Requirement immediate(T value) { return new Immediate<>(value); @@ -18,12 +27,12 @@ public static Requirement from(Supplier supplier) { return new Suppliable<>(supplier); } - private static final class Immediate extends Requirement { + public static final class Immediate implements Requirement { private final V value; @Override - boolean isSatisfied() { + public boolean isSatisfied() { return true; } @@ -39,12 +48,12 @@ public V get() { } - private static final class Suppliable extends Requirement { + public static final class Suppliable implements Requirement { private final Supplier value; @Override - boolean isSatisfied() { + public boolean isSatisfied() { return value.get() != null; } @@ -60,5 +69,64 @@ public V get() { } + public static final class Unsatified implements Requirement { + + @Override + public boolean isSatisfied() { + return false; + } + + @Override + public V get() { + throw new IllegalStateException("Requirement not satisfied"); + } + } + + public abstract class Delegated implements Requirement { + + abstract Requirement delegate(); + + @Override + public boolean isSatisfied() { + return delegate().isSatisfied(); + } + + @Override + public T get() { + Preconditions.checkState(isSatisfied(), "Requirement was not satisfied"); + return delegate().get(); + } + } + + public final class RetriableDelegate extends Delegated { + + Object maybeDelegate; + + public RetriableDelegate(Supplier> lookup) { + maybeDelegate = lookup; + } + + @Override + Requirement delegate() { + if(maybeDelegate instanceof Requirement) { + return (Requirement) maybeDelegate; + } + if(maybeDelegate instanceof Supplier) { + Requirement result = ((Supplier>) maybeDelegate).get(); + if(result != null) { + maybeDelegate = result; + return (Requirement) result; + } + + } + return unsatisfied(); + } + + } + + static Requirement retriableDelegate(Supplier> lookup) { + return new RetriableDelegate(lookup); + } + } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContext.java index 4e7adc832b4..495dd3f3811 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContext.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContext.java @@ -14,17 +14,14 @@ public interface StatementContext { AxiomItemDefinition definition(); - AxiomStatementBuilder builder(); - void registerAsGlobalItem(AxiomIdentifier typeName) throws AxiomSemanticException; StatementContext createEffectiveChild(AxiomIdentifier axiomIdentifier, V value); Optional optionalValue(); - void replace(Supplier> supplier); + void replace(Requirement> statement); StatementContext parent(); - } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextImpl.java index 986a3bcf51e..2a9ea7950f8 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextImpl.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextImpl.java @@ -15,6 +15,8 @@ import com.evolveum.axiom.lang.api.stmt.AxiomStatement; import com.evolveum.axiom.lang.api.stmt.SourceLocation; import com.evolveum.axiom.lang.impl.AxiomStatementImpl.Factory; +import com.evolveum.axiom.lang.impl.StatementRuleContext.Action; +import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; @@ -22,20 +24,16 @@ public class StatementContextImpl implements StatementContext, StatementTreeBuilder { + + private final ModelReactorContext reactor; private final AxiomItemDefinition definition; private final StatementContextImpl parent; - private final List> childrenList = new ArrayList<>(); - private final Multimap> children = HashMultimap.create(); - private final List rules = new ArrayList<>(); - - private V value; - - private AxiomStatementBuilder builder; private SourceLocation startLocation; private SourceLocation endLocation; private SourceLocation valueLocation; + private Requirement> result; StatementContextImpl(ModelReactorContext reactor, StatementContextImpl parent, AxiomItemDefinition definition, SourceLocation loc) { @@ -43,35 +41,33 @@ public class StatementContextImpl implements StatementContext, StatementTr this.reactor = reactor; this.definition = definition; this.startLocation = loc; - this.builder = new AxiomStatementBuilder<>(definition.name(), reactor.typeFactory(definition.type())); + AxiomStatementBuilder builder = new AxiomStatementBuilder<>(definition.name(), reactor.typeFactory(definition.type())); + this.result = new StatementContextResult<>(definition, builder); } @Override public void setValue(Object value) { - this.value = (V) value; - this.builder.setValue((V) value); + mutableResult().setValue((V) value); + } + + private StatementContextResult mutableResult() { + if(result instanceof StatementContextResult) { + return (StatementContextResult) result; + } + throw new IllegalStateException("Result is not mutable"); } boolean isChildAllowed(AxiomIdentifier child) { return definition.type().item(child).isPresent(); } - public StatementContextImpl createChild(AxiomIdentifier child, SourceLocation loc) { - StatementContextImpl childCtx = new StatementContextImpl(reactor, this, childDef(child).get(), loc); - childrenList.add(childCtx); - children.put(child, childCtx); - builder.add(child, childCtx.build()); - return childCtx; - } + @SuppressWarnings("rawtypes") @Override - public StatementTreeBuilder createChildNode(AxiomIdentifier identifier, SourceLocation loc) { - return createChild(identifier, loc); - } - - - Supplier> build() { - return Lazy.from(builder); + public StatementContextImpl createChildNode(AxiomIdentifier identifier, SourceLocation loc) { + StatementContextImpl childCtx = new StatementContextImpl(reactor, this, childDef(identifier).get(), loc); + mutableResult().add(childCtx); + return childCtx; } @Override @@ -91,7 +87,7 @@ public AxiomIdentifier identifier() { @Override public V requireValue() throws AxiomSemanticException { - return AxiomSemanticException.checkNotNull(value, definition, "must have argument specified."); + return AxiomSemanticException.checkNotNull(mutableResult().value(), definition, "must have argument specified."); } @Override @@ -99,133 +95,31 @@ public AxiomItemDefinition definition() { return definition; } - @Override - public AxiomStatementBuilder builder() { - return builder; - } - @Override public void registerAsGlobalItem(AxiomIdentifier typeName) throws AxiomSemanticException { reactor.registerGlobalItem(typeName, this); } - public boolean isCompleted() { - for (RuleContextImpl rule : rules) { - if(!rule.isApplied()) { - return false; - } - } - return true; - } @Override public StatementContext createEffectiveChild(AxiomIdentifier axiomIdentifier, V value) { - StatementContextImpl child = createChild(axiomIdentifier, null); + StatementContextImpl child = createChildNode(axiomIdentifier, null); child.setValue(value); return child; } - class RuleContextImpl implements StatementRuleContext { - - private final StatementRule rule; - private final List> requirements = new ArrayList<>(); - private Action action; - private Supplier errorReport = () -> null; - private boolean applied = false; - + public void registerRule(StatementRuleContextImpl rule) { + mutableResult().addRule(rule); + this.reactor.addOutstanding(rule); + } - public RuleContextImpl(StatementRule rule) { - this.rule = rule; - } - - public StatementRule rule() { - return rule; - } - - @Override - public Optional optionalChildValue(AxiomItemDefinition child, Class type) { - return (Optional) firstChild(child).flatMap(v -> v.optionalValue()); - } - - @Override - public Requirement>> requireGlobalItem(AxiomItemDefinition typeDefinition, - AxiomIdentifier axiomIdentifier) { - return requirement(reactor.requireGlobalItem(axiomIdentifier)); - } - - private Requirement requirement(Requirement req) { - this.requirements.add(req); - return req; - } - - @Override - public RuleContextImpl apply(Action action) { - this.action = action; - registerRule(this); - return this; - } - - public boolean canProcess() { - for (Requirement requirement : requirements) { - if(!requirement.isSatisfied()) { - return false; - } - } - return true; - } - - public void perform() throws AxiomSemanticException { - this.action.apply(StatementContextImpl.this); - this.applied = true; - } - - @Override - public V requiredChildValue(AxiomItemDefinition supertypeReference, Class type) throws AxiomSemanticException { - return null; - } - - @Override - public V requireValue() throws AxiomSemanticException { - return StatementContextImpl.this.requireValue(); - } - - public boolean isApplied() { - return applied; - } - - - - @Override - public StatementRuleContext errorMessage(Supplier errorFactory) { - this.errorReport = errorFactory; - return this; - } - - RuleErrorMessage errorMessage() { - return errorReport.get(); - } - - @Override - public String toString() { - return StatementContextImpl.this.toString() + ":" + rule; - } - - @Override - public AxiomTypeDefinition typeDefinition() { - return definition.type(); - } - - @Override - public RuleErrorMessage error(String format, Object... arguments) { - return RuleErrorMessage.from(startLocation, format, arguments); - } - + public SourceLocation startLocation() { + return startLocation; } - public void registerRule(StatementContextImpl.RuleContextImpl rule) { - this.rules.add(rule); - this.reactor.addOutstanding(rule); + ModelReactorContext reactor() { + return reactor; } public Optional> firstChild(AxiomItemDefinition child) { @@ -233,22 +127,21 @@ public Optional> firstChild(AxiomItemDefinition child) { } private Collection> children(AxiomIdentifier identifier) { - return (Collection) children.get(identifier); + return (Collection) mutableResult().get(identifier); } public void addRule(StatementRule statementRule) throws AxiomSemanticException { - statementRule.apply(new RuleContextImpl(statementRule)); + statementRule.apply(new StatementRuleContextImpl(this,statementRule)); } @Override public Optional optionalValue() { - return Optional.ofNullable(value); + return Optional.ofNullable(mutableResult().value()); } @Override - public void replace(Supplier> supplier) { - // TODO Auto-generated method stub - + public void replace(Requirement> supplier) { + this.result = (Requirement) supplier; } @Override @@ -262,4 +155,21 @@ public void setValue(Object value, SourceLocation loc) { this.valueLocation = loc; } + public Requirement> asRequirement() { + if (result instanceof StatementContextResult) { + return new Deffered<>(result); + } + return result; + } + + public Supplier> asLazy() { + return Lazy.from(() -> result.get()); + } + + public boolean isSatisfied() { + return result.isSatisfied(); + } + + + } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextResult.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextResult.java new file mode 100644 index 00000000000..e2d292bf05b --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextResult.java @@ -0,0 +1,69 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.lang.api.AxiomItemDefinition; +import com.evolveum.axiom.lang.api.stmt.AxiomStatement; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +class StatementContextResult implements Requirement> { + + private V value; + private final List> childrenList = new ArrayList<>(); + private final Multimap> children = HashMultimap.create(); + private AxiomStatementBuilder builder; + + private final List> rules = new ArrayList<>(); + + public StatementContextResult(AxiomItemDefinition definition, AxiomStatementBuilder builder) { + super(); + this.builder = builder; + } + + public void setValue(V value) { + this.value = value; + this.builder.setValue(value); + } + + public void add(StatementContextImpl childCtx) { + childrenList.add(childCtx); + children.put(childCtx.identifier(), childCtx); + builder.add(childCtx.identifier(), childCtx.asLazy()); + } + public V value() { + return value; + } + + public Collection> get(AxiomIdentifier identifier) { + return children.get(identifier); + } + + @Override + public boolean isSatisfied() { + for (StatementRuleContextImpl rule : rules) { + if(!rule.isApplied()) { + return false; + } + } + for (StatementContextImpl rule : childrenList) { + if(!rule.isSatisfied()) { + return false; + } + } + return true; + } + + @Override + public AxiomStatement get() { + return builder.get(); + } + + public void addRule(StatementRuleContextImpl rule) { + this.rules.add(rule); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContext.java index 8aacec29186..ba8f186280c 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContext.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContext.java @@ -17,7 +17,7 @@ public interface StatementRuleContext { V requireValue() throws AxiomSemanticException; - Requirement>> requireGlobalItem(AxiomItemDefinition typeDefinition, AxiomIdentifier axiomIdentifier); + Requirement> requireGlobalItem(AxiomItemDefinition typeDefinition, AxiomIdentifier axiomIdentifier); StatementRuleContext apply(StatementRuleContext.Action action); diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContextImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContextImpl.java new file mode 100644 index 00000000000..f96a2e2b430 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContextImpl.java @@ -0,0 +1,107 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.lang.api.AxiomItemDefinition; +import com.evolveum.axiom.lang.api.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.stmt.AxiomStatement; +public class StatementRuleContextImpl implements StatementRuleContext { + + private final StatementContextImpl context; + private final StatementRule rule; + private final List> requirements = new ArrayList<>(); + private Action action; + private Supplier errorReport = () -> null; + private boolean applied = false; + + public StatementRuleContextImpl(StatementContextImpl context, StatementRule rule) { + this.context = context; + this.rule = rule; + } + + public StatementRule rule() { + return rule; + } + + @Override + public Optional optionalChildValue(AxiomItemDefinition child, Class type) { + return (Optional) context.firstChild(child).flatMap(v -> v.optionalValue()); + } + + @Override + public Requirement> requireGlobalItem(AxiomItemDefinition typeDefinition, + AxiomIdentifier axiomIdentifier) { + return requirement(context.reactor().requireGlobalItem(axiomIdentifier)); + } + + private Requirement requirement(Requirement req) { + this.requirements.add(req); + return req; + } + + @Override + public StatementRuleContextImpl apply(Action action) { + this.action = action; + context.registerRule(this); + return this; + } + + public boolean canProcess() { + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied()) { + return false; + } + } + return true; + } + + public void perform() throws AxiomSemanticException { + this.action.apply(context); + this.applied = true; + } + + @Override + public V requiredChildValue(AxiomItemDefinition supertypeReference, Class type) + throws AxiomSemanticException { + return null; + } + + @Override + public V requireValue() throws AxiomSemanticException { + return context.requireValue(); + } + + public boolean isApplied() { + return applied; + } + + @Override + public StatementRuleContext errorMessage(Supplier errorFactory) { + this.errorReport = errorFactory; + return this; + } + + RuleErrorMessage errorMessage() { + return errorReport.get(); + } + + @Override + public String toString() { + return context.toString() + ":" + rule; + } + + @Override + public AxiomTypeDefinition typeDefinition() { + return context.definition().type(); + } + + @Override + public RuleErrorMessage error(String format, Object... arguments) { + return RuleErrorMessage.from(context.startLocation(), format, arguments); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementTreeBuilder.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementTreeBuilder.java index cc471dd4bc1..37154cda505 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementTreeBuilder.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementTreeBuilder.java @@ -18,4 +18,5 @@ public interface StatementTreeBuilder { StatementTreeBuilder createChildNode(AxiomIdentifier identifier, SourceLocation loc); + } diff --git a/infra/axiom/src/main/resources/axiom-lang.axiom b/infra/axiom/src/main/resources/axiom-lang.axiom index 4f857e0d321..82067f70243 100644 --- a/infra/axiom/src/main/resources/axiom-lang.axiom +++ b/infra/axiom/src/main/resources/axiom-lang.axiom @@ -24,6 +24,12 @@ model axiom-lang { item object { type AxiomObjectDefinition; } + item namespace { + type string; + } + item version { + type string; + } } type AxiomBaseDefinition { @@ -61,6 +67,9 @@ model axiom-lang { type AxiomObjectDefinition { extends AxiomTypeDefinition; + item itemName { + type AxiomIdentifier; // This should be probably at dif + } } type AxiomIdentifier { @@ -73,8 +82,13 @@ model axiom-lang { type AxiomItemDefinition { extends AxiomBaseDefinition; item type { - //type AxiomTypeReference; - type AxiomIdentifier; + type AxiomTypeReference; + } + item minOccurs { + type string; + } + item maxOccurs { + type string; } } diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomParser.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomParser.java index 76bf9724ec5..98d0930bef9 100644 --- a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomParser.java +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomParser.java @@ -16,6 +16,7 @@ import java.io.InputStream; import java.util.Collection; import java.util.List; +import java.util.Optional; import org.testng.annotations.Test; @@ -23,6 +24,7 @@ import com.evolveum.axiom.api.AxiomIdentifier; import com.evolveum.axiom.lang.api.AxiomBuiltIn; import com.evolveum.axiom.lang.api.AxiomItemDefinition; +import com.evolveum.axiom.lang.api.AxiomSchemaContext; import com.evolveum.axiom.lang.api.AxiomTypeDefinition; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Type; @@ -44,41 +46,29 @@ public class TestAxiomParser extends AbstractUnitTest { private static final String NAME = "base-example.axiom"; private static final String AXIOM_LANG = "/axiom-lang.axiom"; - - @Test - public void axiomLanguageDefTest() throws IOException, AxiomSyntaxException { - List> roots = parseInputStream(AXIOM_LANG,AxiomBuiltIn.class.getResourceAsStream(AXIOM_LANG)); - assertNotNull(roots); - } - - @Test public void axiomSelfDescribingTest() throws IOException, AxiomSyntaxException { - ModelReactorContext bootstrapContext =createReactor(Item.MODEL_DEFINITION); + ModelReactorContext bootstrapContext = ModelReactorContext.boostrapReactor(); InputStream stream = AxiomBuiltIn.class.getResourceAsStream(AXIOM_LANG); - bootstrapContext.addStatementFactory(Type.TYPE_DEFINITION.name(), AxiomTypeDefinitionImpl.FACTORY); - bootstrapContext.addStatementFactory(Type.ITEM_DEFINITION.name(), AxiomItemDefinitionImpl.FACTORY); - AxiomStatementSource statementSource = AxiomStatementSource.from(AXIOM_LANG, stream); bootstrapContext.loadModelFromSource(statementSource); - List> roots = bootstrapContext.process(); - assertNotNull(roots); - AxiomStatement model = roots.get(0); - Collection> typeDefs = model.children(Item.TYPE_DEFINITION.name()); - for (AxiomStatement typeDef : typeDefs) { - assertInstanceOf(AxiomTypeDefinition.class, typeDef); - } - AxiomItemDefinition modelDef = model.first(Item.ROOT_DEFINITION.name(), AxiomItemDefinition.class).get(); + AxiomSchemaContext modelContext = bootstrapContext.computeSchemaContext(); + assertTypedefBasetype(modelContext.getType(Type.TYPE_DEFINITION.name())); + + AxiomItemDefinition modelDef = modelContext.getRoot(Item.MODEL_DEFINITION.name()).get(); assertEquals(modelDef.name(), Item.MODEL_DEFINITION.name()); - ModelReactorContext folowupContext = createReactor(modelDef); + ModelReactorContext folowupContext = ModelReactorContext.reactor(modelContext); folowupContext.loadModelFromSource(statementSource); - List> folowupRoots = bootstrapContext.process(); - assertNotNull(roots); - AxiomStatement root = roots.get(0); - AxiomTypeDefinition typeDef = root.children(Item.TYPE_DEFINITION.name()).stream() - .filter(t -> Type.TYPE_DEFINITION.name().equals(t.firstValue(Item.NAME.name(), AxiomIdentifier.class).get())).findFirst().map(AxiomTypeDefinition.class::cast).get(); + AxiomSchemaContext selfparsedContext = bootstrapContext.computeSchemaContext(); + assertNotNull(selfparsedContext.getRoot(Item.MODEL_DEFINITION.name())); + + } + + + private void assertTypedefBasetype(Optional optional) { + AxiomTypeDefinition typeDef = optional.get(); assertNotNull(typeDef); assertEquals(typeDef.superType().get().name(), Type.BASE_DEFINITION.name()); } @@ -92,37 +82,25 @@ private void assertInstanceOf(Class clz, Object value) { @Test public void moduleHeaderTest() throws IOException, AxiomSyntaxException { - List> roots = parseFile(NAME); - AxiomStatement root = roots.get(0); - assertNotNull(root); - assertEquals(root.keyword(), Item.MODEL_DEFINITION.name()); - assertNotNull(root.first(Item.DOCUMENTATION).get().value()); - assertEquals(root.first(Item.TYPE_DEFINITION).get().first(Item.IDENTIFIER).get().value(), AxiomIdentifier.axiom("Example")); - + AxiomSchemaContext context = parseFile(NAME); + assertNotNull(context.getType(AxiomIdentifier.axiom("Example")).get()); } - private List> parseFile(String name) throws AxiomSyntaxException, FileNotFoundException, IOException { + private AxiomSchemaContext parseFile(String name) throws AxiomSyntaxException, FileNotFoundException, IOException { return parseInputStream(name, new FileInputStream(COMMON_DIR_PATH + name)); } - private List> parseInputStream(String name, InputStream stream) throws AxiomSyntaxException, FileNotFoundException, IOException { + private AxiomSchemaContext parseInputStream(String name, InputStream stream) throws AxiomSyntaxException, FileNotFoundException, IOException { return parseInputStream(name, stream, AxiomBuiltIn.Item.MODEL_DEFINITION); } - private List> parseInputStream(String name, InputStream stream, AxiomItemDefinition rootItemDefinition) throws AxiomSyntaxException, FileNotFoundException, IOException { - - ModelReactorContext reactorContext =createReactor(rootItemDefinition); + private AxiomSchemaContext parseInputStream(String name, InputStream stream, AxiomItemDefinition rootItemDefinition) throws AxiomSyntaxException, FileNotFoundException, IOException { + ModelReactorContext reactorContext =ModelReactorContext.defaultReactor(); AxiomStatementSource statementSource = AxiomStatementSource.from(name, stream); reactorContext.loadModelFromSource(statementSource); - List> roots = reactorContext.process(); - return roots; + return reactorContext.computeSchemaContext(); } - private ModelReactorContext createReactor(AxiomItemDefinition rootItemDefinition) { - ModelReactorContext reactorContext = new ModelReactorContext(); - reactorContext.addRules(BasicStatementRule.values()); - reactorContext.addRootItemDef(rootItemDefinition); - return reactorContext; - } + } diff --git a/infra/axiom/src/test/resources/base-example.axiom b/infra/axiom/src/test/resources/base-example.axiom index f18ec0ee151..78ac0b4b18a 100644 --- a/infra/axiom/src/test/resources/base-example.axiom +++ b/infra/axiom/src/test/resources/base-example.axiom @@ -13,6 +13,11 @@ model model-header { namespace "https://ns.evolveum.com/example/axiom/model-header"; version "0.0.1"; + type string; + type PolyString; + type Address; + type Link; + type Example { documentation """ Example complex type. This type does not have supertype. @@ -20,27 +25,23 @@ model model-header { """; - property name { + item name { type string; } - - container nested { - type AxiomTypeDefinition; - } } object User { itemName user; - container address { // shorthand syntax 'container address type Address'; + item address { // shorthand syntax 'container address type Address'; type Address; identifier type; // All values should have unique value. maxOccurs unbounded; } - property name { + item name { type PolyString; } - objectReference link { - targetType Shadow; + item link { + type Link; maxOccurs unbounded; } }