diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java index 4e3d01117c4..8b211ca43d1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java @@ -2697,6 +2697,10 @@ public PrismObjectWrapperFactory findObjectWrapperFact return registry.findWrapperFactory(def); } + public PrismContainerWrapperFactory findContainerWrapperFactory(PrismContainerDefinition def) { + return registry.findContainerWrapperFactory(def); + } + public VW createValueWrapper(IW parentWrapper, PV newValue, ValueStatus status, WrapperContext context) throws SchemaException { ItemWrapperFactory factory = registry.findWrapperFactory(parentWrapper); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java index 715a79e2bc5..526c01a303f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java @@ -8,10 +8,7 @@ import java.util.List; -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.prism.PrismContainerDefinition; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.Referencable; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.util.exception.SchemaException; @@ -22,7 +19,7 @@ * @author katka * */ -public interface PrismContainerValueWrapper extends PrismValueWrapper>{ +public interface PrismContainerValueWrapper extends PrismValueWrapper { String getDisplayName(); String getHelpText(); @@ -31,11 +28,6 @@ public interface PrismContainerValueWrapper extends Pri void setExpanded(boolean expanded); - boolean hasMetadata(); - boolean isShowMetadata(); - - void setShowMetadata(boolean showMetadata); - boolean isSorted(); void setSorted(boolean sorted); @@ -46,9 +38,9 @@ public interface PrismContainerValueWrapper extends Pri List> getContainers(); - List> getNonContainers(); + List> getNonContainers(); - List> getItems(); + List> getItems(); PrismContainerWrapper findContainer(ItemPath path) throws SchemaException; PrismPropertyWrapper findProperty(ItemPath propertyPath) throws SchemaException; @@ -60,7 +52,6 @@ public interface PrismContainerValueWrapper extends Pri boolean isSelected(); boolean setSelected(boolean selected); //TODO why return boolean? - boolean isReadOnly(); void setReadOnly(boolean readOnly, boolean recursive); @@ -83,4 +74,7 @@ public interface PrismContainerValueWrapper extends Pri PrismContainerDefinition getDefinition(); + @Override + PrismContainerValue getNewValue(); } + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismObjectValueWrapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismObjectValueWrapper.java index 5a8de9c45f2..33d02dd658b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismObjectValueWrapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismObjectValueWrapper.java @@ -6,6 +6,8 @@ */ package com.evolveum.midpoint.gui.api.prism.wrapper; +import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.PrismObjectValue; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** @@ -14,6 +16,6 @@ */ public interface PrismObjectValueWrapper extends PrismContainerValueWrapper { - - + @Override + PrismContainerValue getNewValue(); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismValueWrapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismValueWrapper.java index 23a69128fe7..20746194a1e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismValueWrapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismValueWrapper.java @@ -8,6 +8,7 @@ import java.io.Serializable; +import com.evolveum.midpoint.gui.impl.prism.wrapper.ValueMetadataWrapperImpl; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ItemDelta; @@ -19,7 +20,7 @@ * @author katka * */ -public interface PrismValueWrapper extends Serializable, DebugDumpable { +public interface PrismValueWrapper extends Serializable, DebugDumpable { T getRealValue(); void setRealValue(T realValue); @@ -27,12 +28,20 @@ public interface PrismValueWrapper extends Serializable ValueStatus getStatus(); void setStatus(ValueStatus status); - V getNewValue(); - V getOldValue(); + V getNewValue(); + V getOldValue(); IW getParent(); - > void addToDelta(D delta) throws SchemaException; + > void addToDelta(D delta) throws SchemaException; boolean isVisible(); + PrismContainerValueWrapper getValueMetadata(); + void setValueMetadata(ValueMetadataWrapperImpl valueMetadata); + + boolean isShowMetadata(); + void setShowMetadata(boolean showMetadata); + + String toShortString(); + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java index a5694982a59..5e776eac525 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java @@ -29,7 +29,7 @@ /** * @author skublik */ -public class PrismReferenceWrapperColumn extends AbstractItemWrapperColumn> { +public class PrismReferenceWrapperColumn extends AbstractItemWrapperColumn> { private static final long serialVersionUID = 1L; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumnPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumnPanel.java index 9e3317a0bd0..f10b5156920 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumnPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumnPanel.java @@ -27,7 +27,7 @@ * @author katka * */ -public class PrismReferenceWrapperColumnPanel extends AbstractItemWrapperColumnPanel, PrismValueWrapper> { +public class PrismReferenceWrapperColumnPanel extends AbstractItemWrapperColumnPanel, PrismValueWrapper> { private static final long serialVersionUID = 1L; private static final Trace LOGGER = TraceManager.getTrace(PrismReferenceWrapperColumnPanel.class); @@ -37,12 +37,12 @@ public class PrismReferenceWrapperColumnPanel extends Ab } @Override - protected String createLabel(PrismValueWrapper object) { + protected String createLabel(PrismValueWrapper object) { return WebComponentUtil.getReferencedObjectDisplayNamesAndNames(object.getRealValue(), false, true); } @Override - protected Panel createValuePanel(String id, IModel> model, PrismValueWrapper object) { + protected Panel createValuePanel(String id, IModel> model, PrismValueWrapper object) { Panel panel; try { @@ -57,7 +57,7 @@ protected Panel createValuePanel(String id, IModel> mod } @Override - protected Panel createLink(String id, IModel> object) { + protected Panel createLink(String id, IModel> object) { LinkPanel linkPanel = new LinkPanel(id, new IModel() { @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemPanelContext.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemPanelContext.java index d3f8970b545..3139099734f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemPanelContext.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemPanelContext.java @@ -87,7 +87,7 @@ public ItemRealValueModel getRealValueModel() { return realValueModel; } - public > void setRealValueModel(IModel valueWrapper) { + public > void setRealValueModel(IModel valueWrapper) { this.realValueModel = new ItemRealValueModel<>(valueWrapper); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemRealValueModel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemRealValueModel.java index 148313f090f..23a0926b869 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemRealValueModel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/ItemRealValueModel.java @@ -17,7 +17,7 @@ public class ItemRealValueModel extends PropertyModel{ private static final long serialVersionUID = 1L; - public ItemRealValueModel(IModel> modelObject) { + public ItemRealValueModel(IModel> modelObject) { super(modelObject, "realValue"); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/PrismContainerPanelContext.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/PrismContainerPanelContext.java index 8d065bf2b18..7adf23bb6c4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/PrismContainerPanelContext.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/PrismContainerPanelContext.java @@ -27,7 +27,7 @@ public PrismContainerPanelContext(IModel> itemWrapper) } @Override - public > void setRealValueModel(IModel valueWrapper) { + public > void setRealValueModel(IModel valueWrapper) { super.setRealValueModel(valueWrapper); this.valueWrapperModel = (IModel>) valueWrapper; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java index 070a244387a..cfd25c285f8 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java @@ -9,9 +9,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; +import com.evolveum.midpoint.gui.impl.prism.wrapper.ValueMetadataWrapperImpl; import com.evolveum.midpoint.model.api.ModelService; import com.evolveum.midpoint.prism.path.ItemName; @@ -154,7 +157,7 @@ private boolean skipCreateWrapper(ItemDefinition def, ItemStatus status, Wrap } protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { - if (def.isOperational()) { + if (def.isOperational() && !context.isCreateOperational()) { LOGGER.trace("Skipping creating wrapper for {}, because it is operational.", def.getItemName()); return false; } @@ -173,6 +176,7 @@ protected List createValuesWrapper(IW itemWrapper, I item, WrapperContext co if (shouldCreateEmptyValue(item, context)) { PV prismValue = createNewValue(item); VW valueWrapper = createValueWrapper(itemWrapper, prismValue, ValueStatus.ADDED, context); +// setupMetadata(valueWrapper, context); pvWrappers.add(valueWrapper); } return pvWrappers; @@ -181,6 +185,7 @@ protected List createValuesWrapper(IW itemWrapper, I item, WrapperContext co for (PV pcv : values) { if(canCreateValueWrapper(pcv)){ VW valueWrapper = createValueWrapper(itemWrapper, pcv, ValueStatus.NOT_CHANGED, context); + setupMetadata(valueWrapper, context); pvWrappers.add(valueWrapper); } } @@ -189,6 +194,21 @@ protected List createValuesWrapper(IW itemWrapper, I item, WrapperContext co } + protected void setupMetadata(VW valueWrapper, WrapperContext ctx) throws SchemaException { + PrismValue oldValue = valueWrapper.getNewValue(); + Optional metadata = oldValue.valueMetadata(); + if (!metadata.isPresent()) { + LOGGER.trace("Skipping creating metadata"); + return; + } + + ValueMetadata valueMetadata = metadata.get(); + + ValueMetadataWrapperFactoryImpl valueMetadataWrapperFactory = new ValueMetadataWrapperFactoryImpl(getRegistry()); + PrismContainerValueWrapper valueMetadataWrapper = valueMetadataWrapperFactory.createValueWrapper(null, valueMetadata, ValueStatus.NOT_CHANGED, ctx); + valueWrapper.setValueMetadata(new ValueMetadataWrapperImpl(valueMetadataWrapper)); + } + protected List getValues(I item) { return item.getValues(); } @@ -261,7 +281,7 @@ protected boolean shouldCreateEmptyValue(I item, WrapperContext context) { /** * @return the registry */ - public GuiComponentRegistryImpl getRegistry() { + public GuiComponentRegistry getRegistry() { return registry; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java index 53800abae53..bd2a0710517 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java @@ -27,7 +27,7 @@ public class OperationalContainerWrapperFactory extends PrismContainerWrapperFac @Override public boolean match(ItemDefinition def) { - return QNameUtil.match(MetadataType.COMPLEX_TYPE, def.getTypeName()) || QNameUtil.match(TriggerType.COMPLEX_TYPE, def.getTypeName()); + return QNameUtil.match(TriggerType.COMPLEX_TYPE, def.getTypeName()); //QNameUtil.match(MetadataType.COMPLEX_TYPE, def.getTypeName()) || } @PostConstruct diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java index 21e5440ae32..f0975820a2c 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java @@ -66,7 +66,9 @@ public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper containerValueWrapper.getItems().addAll((Collection) children); containerValueWrapper.setVirtualContainerItems(context.getVirtualItemSpecification()); - parent.setVirtual(context.getVirtualItemSpecification() != null); + if (parent != null) { + parent.setVirtual(context.getVirtualItemSpecification() != null); + } return containerValueWrapper; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java index 0a66b7a906f..2e05ead9c5e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java @@ -70,6 +70,7 @@ public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStat context.setShowEmpty(ItemStatus.ADDED == status); objectWrapper.setExpanded(true); PrismContainerValueWrapper valueWrapper = createValueWrapper(objectWrapper, object.getValue(), ItemStatus.ADDED == status ? ValueStatus.ADDED : ValueStatus.NOT_CHANGED, context); + setupMetadata(valueWrapper, context); objectWrapper.getValues().add(valueWrapper); registerWrapperPanel(objectWrapper); @@ -97,6 +98,7 @@ public void updateWrapper(PrismObjectWrapper wrapper, WrapperContext context) wrapper.getValue().getItems().clear(); PrismContainerValueWrapper valueWrapper = createValueWrapper(wrapper, wrapper.getObject().getValue(), ItemStatus.ADDED == wrapper.getStatus() ? ValueStatus.ADDED : ValueStatus.NOT_CHANGED, context); + setupMetadata(valueWrapper, context); wrapper.getValues().clear(); wrapper.getValues().add(valueWrapper); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ValueMetadataWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ValueMetadataWrapperFactoryImpl.java new file mode 100644 index 00000000000..099aaa3824a --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ValueMetadataWrapperFactoryImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.gui.impl.factory.wrapper; + +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.gui.api.prism.ItemStatus; +import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.PrismContainer; +import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.web.component.prism.ValueStatus; + +import java.util.List; + +public class ValueMetadataWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { + + private GuiComponentRegistry registry; + + ValueMetadataWrapperFactoryImpl(GuiComponentRegistry registry) { + this.registry = registry; + } + + @Override + public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, PrismContainerValue value, ValueStatus status, WrapperContext context) throws SchemaException { + context.setCreateOperational(true); + PrismContainerValueWrapper v = super.createValueWrapper(parent, value, status, context); + context.setCreateOperational(false); + return v; + } + + @Override + protected boolean shouldBeExpanded(PrismContainerWrapper parent, PrismContainerValue value, WrapperContext context) { + return true; + } + + @Override + protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { + return false; + } + + @Override + protected List getItemDefinitions(PrismContainerWrapper parent, PrismContainerValue value) { + return value.getComplexTypeDefinition().getDefinitions(); + } + + @Override + public GuiComponentRegistry getRegistry() { + return registry; + } + + @Override + protected ItemWrapper createChildWrapper(ItemDefinition def, PrismContainerValueWrapper containerValueWrapper, WrapperContext context) throws SchemaException { + ItemWrapper child = super.createChildWrapper(def, containerValueWrapper, context); + //TODO ugly hack. find out something smarter + if (ItemStatus.ADDED == child.getStatus()) { + return null; + } + + return child; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ExpressionPropertyPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ExpressionPropertyPanel.java index 9435485fc6f..b62e180bb42 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ExpressionPropertyPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ExpressionPropertyPanel.java @@ -19,7 +19,6 @@ import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.list.ListItem; -import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; @@ -53,7 +52,7 @@ public ExpressionPropertyPanel(String id, IModel, IW extends ItemWrapper> extends BasePanel implements RefreshableTabPanel { +public abstract class ItemPanel, IW extends ItemWrapper> extends BasePanel implements RefreshableTabPanel { private static final long serialVersionUID = 1L; @@ -68,7 +68,7 @@ private void initLayout() { getModelObject().revive(getPrismContext()); } - Panel headerPanel = createHeaderPanel(); + Component headerPanel = createHeaderPanel(); headerPanel.add(new VisibleBehaviour(() -> getHeaderVisibility())); add(headerPanel); @@ -84,7 +84,7 @@ protected boolean getHeaderVisibility() { return getParent().findParent(AbstractItemWrapperColumnPanel.class) == null; } - protected abstract Panel createHeaderPanel(); + protected abstract Component createHeaderPanel(); protected ListView createValuesPanel() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerPanel.html new file mode 100755 index 00000000000..388871d5e3b --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerPanel.html @@ -0,0 +1,33 @@ + + + + + + +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+ + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerPanel.java new file mode 100644 index 00000000000..52aab3e2a4a --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerPanel.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.gui.impl.prism.panel; + +import com.evolveum.midpoint.gui.api.model.ReadOnlyModel; +import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; + +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismValueWrapper; + +import com.evolveum.midpoint.gui.impl.factory.panel.ItemRealValueModel; + +import com.evolveum.midpoint.web.component.util.VisibleBehaviour; + +import org.apache.wicket.AttributeModifier; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.model.IModel; + +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.util.exception.SchemaException; + +import org.apache.wicket.model.PropertyModel; + +/** + * @author katka + * + */ +public class MetadataContainerPanel extends ItemPanel, PrismContainerWrapper>{ + + private static final long serialVersionUID = 1L; + + private static final String ID_HEADER = "header"; + private static final String ID_PANEL = "panel"; + + private static final String ID_NON_CONTAINERS = "nonContainers"; + private static final String ID_CONTAINERS = "containers"; + private static final String ID_CONTAINER = "container"; + private static final String ID_LABEL = "label"; + private static final String ID_VALUES = "values"; + private static final String ID_VALUE = "value"; + + /** + * @param id + * @param model + */ + public MetadataContainerPanel(String id, IModel> model, ItemPanelSettings settings) { + super(id, model, settings); + } + + @Override + protected void onInitialize() { + super.onInitialize(); + + } + + @Override + protected Component createHeaderPanel() { + return new Label(ID_HEADER, new PropertyModel<>(getModel(), "displayName")); + } + + @Override + protected boolean getHeaderVisibility() { + return true; + } + + @Override + protected Component createValuePanel(ListItem> item) { + + WebMarkupContainer valuePanel = new WebMarkupContainer(ID_PANEL); + item.add(valuePanel); + ListView nonContainers = new ListView(ID_NON_CONTAINERS, new PropertyModel<>(item.getModel(), "nonContainers")) { + + @Override + protected void populateItem(ListItem listItem) { + listItem.add(new Label(ID_LABEL, new PropertyModel<>(listItem.getModel(), "displayName"))); + + ListView values = new ListView(ID_VALUES, new PropertyModel<>(listItem.getModel(), "values")) { + + @Override + protected void populateItem(ListItem listItem) { + Label value = new Label(ID_VALUE, new ReadOnlyModel<>(() -> { + + return listItem.getModelObject().toShortString(); + })); + listItem.add(value); + } + }; + + values.add(new VisibleBehaviour(() -> listItem.getModelObject() != null && !listItem.getModelObject().isEmpty())); + listItem.add(values); + } + }; + valuePanel.add(nonContainers); + + ListView> containers = new ListView<>(ID_CONTAINERS, new PropertyModel<>(item.getModel(), "containers")) { + + @Override + protected void populateItem(ListItem> listItem) { + listItem.add(new MetadataContainerPanel(ID_CONTAINER, listItem.getModel(), getSettings().copy())); + } + }; + valuePanel.add(containers); + + return valuePanel; + + } + + @Override + protected PV createNewValue(PrismContainerWrapper itemWrapper) { + return (PV) itemWrapper.getItem().createNewValue(); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerValuePanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerValuePanel.html new file mode 100644 index 00000000000..aaee0083f1e --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerValuePanel.html @@ -0,0 +1,30 @@ + + + + + +
+
+ +
+
+ + + + + + + + + + +
+
+
+
+ diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerValuePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerValuePanel.java new file mode 100644 index 00000000000..a920b3923ff --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/MetadataContainerValuePanel.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.gui.impl.prism.panel; + +import java.awt.*; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.gui.api.component.tabs.PanelTab; +import com.evolveum.midpoint.gui.impl.prism.wrapper.ValueMetadataWrapperImpl; +import com.evolveum.midpoint.web.component.TabbedPanel; +import com.evolveum.midpoint.web.component.form.Form; + +import com.evolveum.midpoint.web.component.prism.ItemVisibility; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.wicket.AttributeModifier; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.behavior.AttributeAppender; +import org.apache.wicket.extensions.markup.html.tabs.ITab; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.*; + +import com.evolveum.midpoint.gui.api.GuiStyleConstants; +import com.evolveum.midpoint.gui.api.component.togglebutton.ToggleIconButton; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.gui.api.prism.wrapper.*; +import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; +import com.evolveum.midpoint.gui.impl.factory.panel.ItemPanelContext; +import com.evolveum.midpoint.gui.impl.factory.panel.PrismContainerPanelContext; +import com.evolveum.midpoint.gui.impl.prism.panel.component.ListContainersPopup; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.PrismContainerDefinition; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.web.component.AjaxButton; +import com.evolveum.midpoint.web.component.prism.ValueStatus; +import com.evolveum.midpoint.web.component.util.VisibleBehaviour; +import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; +import com.evolveum.midpoint.web.util.InfoTooltipBehavior; + +/** + * @author katka + * + */ +public class MetadataContainerValuePanel extends PrismValuePanel, ValueMetadataWrapperImpl> { + + private static final String ID_CONTAINER = "container"; + + public MetadataContainerValuePanel(String id, IModel model, ItemPanelSettings settings) { + super(id, model, settings); + } + + + @Override + protected Component createDefaultPanel(String id) { + + if (getModelObject() == null) { + return new WebMarkupContainer(id); + } + + ListView containers = new ListView(id, new PropertyModel<>(getModel(), "containers")) { + + @Override + protected void populateItem(ListItem listItem) { + listItem.add(new MetadataContainerPanel(ID_CONTAINER, listItem.getModel(), getSettings().copy())); + } + }; + + add(containers); + + return containers; + } + + @Override + protected PV createNewValue(PrismContainerWrapper itemWrapper) { + return null; + } + + @Override + protected void removeValue(ValueMetadataWrapperImpl valueToRemove, AjaxRequestTarget target) throws SchemaException { + + } + + private List createTabs() { + List tabs = new ArrayList<>(); + + for (PrismContainerWrapper w : getModelObject().getContainers()) { + tabs.add(new PanelTab(createStringResource(w.getDisplayName())) { + @Override + public WebMarkupContainer createPanel(String panelId) { + ItemPanelSettings settings = getSettings().copy(); + settings.setHeaderVisible(false); + return new MetadataContainerPanel<>(panelId, Model.of(w), settings); + } + }); + } + + if (CollectionUtils.isNotEmpty(getModelObject().getNonContainers())) { + tabs.add(new PanelTab(createStringResource(getModelObject().getDisplayName())) { + @Override + public WebMarkupContainer createPanel(String panelId) { + ItemPanelSettings s = getSettings().copy(); + s.setVisibilityHandler(wrapper -> { + if (wrapper instanceof PrismContainerWrapper) { + return ItemVisibility.HIDDEN; + } + return ItemVisibility.AUTO; + }); + return new PrismContainerValuePanel<>(panelId, getModel(), s) { + + }; + } + }); + } + return tabs; + } + + @Override + protected void createMetadataPanel(Form form) { + + } + + @Override + protected PC createPanelCtx(IModel> wrapper) { + return (PC) new PrismContainerPanelContext(wrapper); + } + + @Override + protected boolean isRemoveButtonVisible() { + return false; + } + +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerPanel.java index cb475ed5b9a..ef8e0da87e5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerPanel.java @@ -14,7 +14,6 @@ import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.list.ListItem; -import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; @@ -54,7 +53,7 @@ protected void onInitialize() { } @Override - protected Panel createHeaderPanel() { + protected Component createHeaderPanel() { return new PrismContainerHeaderPanel(ID_HEADER, getModel()) { @Override protected void onExpandClick(AjaxRequestTarget target) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.html index cdaa0327134..48fc60a2f9d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.html @@ -18,11 +18,14 @@
- + - + + +
+
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java index 9e9791bf2dc..dca9c0fb189 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java @@ -131,7 +131,7 @@ public void onClick(AjaxRequestTarget target) { } protected LoadableDetachableModel getLabelModel() { - return getPageBase().createStringResource(getModel().getObject().getDisplayName()); + return getPageBase().createStringResource("${displayName}", getModel()); } @Override @@ -338,7 +338,7 @@ public String getObject() { private void initButtons(WebMarkupContainer header) { header.add(createExpandCollapseButton()); - header.add(createMetadataButton()); +// header.add(createMetadataButton()); header.add(createSortButton()); header.add(createAddMoreButton()); } @@ -360,29 +360,29 @@ protected Label getHelpLabel() { return help; } - private ToggleIconButton createMetadataButton() { - ToggleIconButton showMetadataButton = new ToggleIconButton(ID_SHOW_METADATA, - GuiStyleConstants.CLASS_ICON_SHOW_METADATA, GuiStyleConstants.CLASS_ICON_SHOW_METADATA) { - private static final long serialVersionUID = 1L; - - @Override - public void onClick(AjaxRequestTarget target) { - onShowMetadataClicked(target); - } - - @Override - public boolean isOn() { - return PrismContainerValuePanel.this.getModelObject().isShowMetadata(); - } - - - }; - showMetadataButton.add(new AttributeModifier("title", new StringResourceModel("PrismContainerValuePanel.showMetadata.${showMetadata}", getModel()))); - showMetadataButton.add(new VisibleBehaviour(() -> getModelObject().hasMetadata() && shouldBeButtonsShown())); - showMetadataButton.setOutputMarkupId(true); - showMetadataButton.setOutputMarkupPlaceholderTag(true); - return showMetadataButton; - } +// private ToggleIconButton createMetadataButton() { +// ToggleIconButton showMetadataButton = new ToggleIconButton(ID_SHOW_METADATA, +// GuiStyleConstants.CLASS_ICON_SHOW_METADATA, GuiStyleConstants.CLASS_ICON_SHOW_METADATA) { +// private static final long serialVersionUID = 1L; +// +// @Override +// public void onClick(AjaxRequestTarget target) { +// onShowMetadataClicked(target); +// } +// +// @Override +// public boolean isOn() { +// return PrismContainerValuePanel.this.getModelObject().isShowMetadata(); +// } +// +// +// }; +// showMetadataButton.add(new AttributeModifier("title", new StringResourceModel("PrismContainerValuePanel.showMetadata.${showMetadata}", getModel()))); +// showMetadataButton.add(new VisibleBehaviour(() -> getModelObject().hasMetadata() && shouldBeButtonsShown())); +// showMetadataButton.setOutputMarkupId(true); +// showMetadataButton.setOutputMarkupPlaceholderTag(true); +// return showMetadataButton; +// } private ToggleIconButton createSortButton() { ToggleIconButton sortPropertiesButton = new ToggleIconButton(ID_SORT_PROPERTIES, @@ -494,11 +494,11 @@ private void onSortClicked(AjaxRequestTarget target) { refreshPanel(target); } - private void onShowMetadataClicked(AjaxRequestTarget target) { - CVW wrapper = getModelObject(); - wrapper.setShowMetadata(!wrapper.isShowMetadata()); - refreshPanel(target); - } +// private void onShowMetadataClicked(AjaxRequestTarget target) { +// CVW wrapper = getModelObject(); +// wrapper.setShowMetadata(!wrapper.isShowMetadata()); +// refreshPanel(target); +// } private void refreshPanel(AjaxRequestTarget target) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismPropertyPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismPropertyPanel.java index ae8ed7af1b1..94b14552588 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismPropertyPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismPropertyPanel.java @@ -17,13 +17,12 @@ import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.list.ListItem; -import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; /** * @author katkav */ -public class PrismPropertyPanel extends ItemPanel, PrismPropertyWrapper> { +public class PrismPropertyPanel extends ItemPanel, PrismPropertyWrapper> { private static final long serialVersionUID = 1L; private static final Trace LOGGER = TraceManager.getTrace(PrismPropertyPanel.class); @@ -39,7 +38,7 @@ public PrismPropertyPanel(String id, IModel> model, Item } @Override - protected Panel createHeaderPanel() { + protected Component createHeaderPanel() { return new PrismPropertyHeaderPanel(ID_HEADER, getModel()) { @Override protected void refreshPanel(AjaxRequestTarget target) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismReferencePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismReferencePanel.java index d2386a2ba50..33978de33c0 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismReferencePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismReferencePanel.java @@ -15,7 +15,6 @@ import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.list.ListItem; -import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import com.evolveum.midpoint.prism.Referencable; @@ -38,7 +37,7 @@ public PrismReferencePanel(String id, IModel> model, It } @Override - protected Panel createHeaderPanel() { + protected Component createHeaderPanel() { return new PrismReferenceHeaderPanel(ID_HEADER, getModel()) { @Override protected void refreshPanel(AjaxRequestTarget target) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.html index a20d1661df2..84a72a9594d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.html @@ -22,10 +22,15 @@ +
+
+
+
+
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.java index 8979e498c5a..7731d66b32f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismValuePanel.java @@ -39,7 +39,7 @@ import org.apache.wicket.model.LambdaModel; import org.apache.wicket.model.PropertyModel; -public abstract class PrismValuePanel> extends BasePanel { +public abstract class PrismValuePanel> extends BasePanel { private static final transient Trace LOGGER = TraceManager.getTrace(PrismValuePanel.class); @@ -52,6 +52,8 @@ public abstract class PrismValuePanel isRemoveButtonVisible())); buttonContainer.add(removeButton); + AjaxLink showMetadataButton = new AjaxLink(ID_SHOW_METADATA) { + private static final long serialVersionUID = 1L; + + @Override + public void onClick(AjaxRequestTarget target) { + showMetadataPerformed(PrismValuePanel.this.getModelObject(), target); + } + }; + buttonContainer.add(showMetadataButton); + showMetadataButton.add(new VisibleBehaviour(() -> getModelObject() != null && getModelObject().getValueMetadata() != null)); + addToHeader(buttonContainer); return buttonContainer; } @@ -106,7 +121,10 @@ protected void addToHeader(WebMarkupContainer headerContainer) { private WebMarkupContainer createValuePanel(Form form) { - GuiComponentFactory factory = getPageBase().getRegistry().findValuePanelFactory(getModelObject().getParent()); + GuiComponentFactory factory = null; + if (getModelObject() != null && getModelObject().getParent() != null) { + factory = getPageBase().getRegistry().findValuePanelFactory(getModelObject().getParent()); + } WebMarkupContainer valueContainer = new WebMarkupContainer(ID_VALUE_CONTAINER); valueContainer.setOutputMarkupId(true); form.add(valueContainer); @@ -152,6 +170,12 @@ private WebMarkupContainer createValuePanel(Form form) { } + protected void createMetadataPanel(Form form) { + MetadataContainerValuePanel metadataPanel = new MetadataContainerValuePanel(ID_METADATA, new PropertyModel<>(getModel(), "valueMetadata"), new ItemPanelSettingsBuilder().editabilityHandler(wrapper -> false).build()); + metadataPanel.add(new VisibleBehaviour(() -> getModelObject().getValueMetadata() != null && getModelObject().isShowMetadata())); + form.add(metadataPanel); + } + private AjaxEventBehavior createEventBehavior() { return new AjaxFormComponentUpdatingBehavior("change") { @@ -175,10 +199,10 @@ protected void onError(AjaxRequestTarget target, RuntimeException e) { private VisibleEnableBehaviour createVisibleEnableBehavior() { return new VisibleEnableBehaviour() { -// @Override -// public boolean isVisible() { -// return isVisibleValue(); -// } + @Override + public boolean isVisible() { + return true; + } @Override public boolean isEnabled() { @@ -254,6 +278,11 @@ private O getObject() { //TODO move to the ItemPanel, exception handling protected abstract void removeValue(VW valueToRemove, AjaxRequestTarget target) throws SchemaException; + private void showMetadataPerformed(VW value, AjaxRequestTarget target) { + value.setShowMetadata(!value.isShowMetadata()); + target.add(PrismValuePanel.this); + } + protected boolean isRemoveButtonVisible() { return !getModelObject().getParent().isReadOnly(); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ResourceAttributeDefinitionPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ResourceAttributeDefinitionPanel.java index 7f5bb5ea8d7..9f94a2bb4dd 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ResourceAttributeDefinitionPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/ResourceAttributeDefinitionPanel.java @@ -8,7 +8,8 @@ package com.evolveum.midpoint.gui.impl.prism.panel; import com.evolveum.midpoint.gui.api.prism.wrapper.ResourceAttributeWrapper; -import org.apache.wicket.markup.html.panel.Panel; + +import org.apache.wicket.Component; import org.apache.wicket.model.IModel; import com.evolveum.midpoint.util.logging.Trace; @@ -34,7 +35,7 @@ public ResourceAttributeDefinitionPanel(String id, IModel(ID_HEADER, getResourceAttributeDefinitionModel()); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ItemWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ItemWrapperImpl.java index e63383b8e9a..85d52758bed 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ItemWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ItemWrapperImpl.java @@ -24,7 +24,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import com.evolveum.midpoint.gui.api.page.PageBase; import com.evolveum.midpoint.gui.api.prism.ItemStatus; @@ -75,7 +74,7 @@ public abstract class ItemWrapperImpl parent, I item, ItemStatus status) { + public ItemWrapperImpl(PrismContainerValueWrapper parent, I item, ItemStatus status) { Validate.notNull(item, "Item must not be null."); Validate.notNull(status, "Item status must not be null."); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java index 643047bfbf9..365a05c0007 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java @@ -34,7 +34,7 @@ * @author katka * */ -public class PrismContainerValueWrapperImpl extends PrismValueWrapperImpl> implements PrismContainerValueWrapper { +public class PrismContainerValueWrapperImpl extends PrismValueWrapperImpl implements PrismContainerValueWrapper { private static final long serialVersionUID = 1L; @@ -135,17 +135,17 @@ public void setExpanded(boolean expanded) { this.expanded = expanded; } - @Override - public boolean hasMetadata() { - for (ItemWrapper container : items) { - if (container.getTypeName().equals(MetadataType.COMPLEX_TYPE)) { - return true; - } - } - - return false; - } - +// @Override +// public boolean hasMetadata() { +// for (ItemWrapper container : items) { +// if (container.getTypeName().equals(MetadataType.COMPLEX_TYPE)) { +// return true; +// } +// } +// +// return false; +// } +// @Override public List> getItems() { @@ -484,6 +484,16 @@ public boolean isVisible() { return ((PrismContainerWrapper) parent).isExpanded() || isHeterogenous(); } + @Override + public PrismContainerValue getNewValue() { + return super.getNewValue(); + } + + @Override + public PrismContainerValue getOldValue() { + return super.getOldValue(); + } + public PrismContainerDefinition getDefinition() { return getNewValue().getDefinition(); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerWrapperImpl.java index a4f1962521c..51589ecd271 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerWrapperImpl.java @@ -349,9 +349,9 @@ public boolean isVisible(PrismContainerValueWrapper parent, ItemVisibilityHan return false; } - if (getComplexTypeDefinition().getTypeName().equals(MetadataType.COMPLEX_TYPE)) { - return (getParent() != null && getParent().isShowMetadata()); - } +// if (getComplexTypeDefinition().getTypeName().equals(MetadataType.COMPLEX_TYPE)) { +// return (getParent() != null && getParent().isShowMetadata()); +// } // pretend that object is always expanded. it is becasue all other containers are children of it // and it can influence visibility behavior on different tabs. diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismObjectValueWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismObjectValueWrapperImpl.java index 0aecb7a6ed3..7d7e4f0ef3f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismObjectValueWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismObjectValueWrapperImpl.java @@ -10,6 +10,8 @@ import java.util.List; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismObjectValueWrapper; +import com.evolveum.midpoint.prism.PrismContainerValue; + import org.apache.wicket.model.StringResourceModel; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; @@ -57,4 +59,9 @@ public List> getContainers() public String getDisplayName() { return new StringResourceModel("prismContainer.mainPanelDisplayName").getString(); } + + @Override + public PrismContainerValue getNewValue() { + return super.getNewValue(); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismPropertyValueWrapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismPropertyValueWrapper.java index b659fb3a9f2..6e5027e8a1a 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismPropertyValueWrapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismPropertyValueWrapper.java @@ -10,6 +10,7 @@ import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; import com.evolveum.midpoint.prism.PrismPropertyValue; +import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.util.DOMUtil; @@ -21,7 +22,7 @@ * @author katka * */ -public class PrismPropertyValueWrapper extends PrismValueWrapperImpl> { +public class PrismPropertyValueWrapper extends PrismValueWrapperImpl { /** * @param parent @@ -115,4 +116,13 @@ public String toShortString() { return getRealValue().toString(); } + @Override + public PrismPropertyValue getNewValue() { + return super.getNewValue(); + } + + @Override + public PrismPropertyValue getOldValue() { + return super.getOldValue(); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismReferenceValueWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismReferenceValueWrapperImpl.java index 50173d8ba52..1a50307f394 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismReferenceValueWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismReferenceValueWrapperImpl.java @@ -7,15 +7,20 @@ package com.evolveum.midpoint.gui.impl.prism.wrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismReferenceWrapper; +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.Referencable; +import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.web.component.prism.ValueStatus; +import javax.xml.namespace.QName; + /** * @author katka * */ -public class PrismReferenceValueWrapperImpl extends PrismValueWrapperImpl { +public class PrismReferenceValueWrapperImpl extends PrismValueWrapperImpl { private static final long serialVersionUID = 1L; @@ -56,4 +61,28 @@ public boolean isLink() { public void setLink(boolean link) { isLink = link; } + + @Override + public PrismReferenceValue getNewValue() { + return super.getNewValue(); + } + + @Override + public String toShortString() { + T referencable = getRealValue(); + if (referencable == null) { + return ""; + } + + return getRefName(referencable) + " (" + getTargetType(referencable) + ")"; + } + + private String getRefName(T referencable) { + return referencable.getTargetName() != null ? WebComponentUtil.getOrigStringFromPoly(referencable.getTargetName()) : referencable.getOid(); + } + + private String getTargetType(T referencable) { + QName type = referencable.getType(); + return type != null ? type.getLocalPart() : ""; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismValueWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismValueWrapperImpl.java index fb42768de40..08427b28b27 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismValueWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismValueWrapperImpl.java @@ -7,9 +7,11 @@ package com.evolveum.midpoint.gui.impl.prism.wrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismValueWrapper; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.ValueMetadata; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; import com.evolveum.midpoint.util.DOMUtil; @@ -24,36 +26,40 @@ * @author katka * */ -public abstract class PrismValueWrapperImpl implements PrismValueWrapper { +public abstract class PrismValueWrapperImpl implements PrismValueWrapper { private static final long serialVersionUID = 1L; private ItemWrapper parent; - private V oldValue; - private V newValue; + private PrismValue oldValue; + private PrismValue newValue; private ValueStatus status; + private ValueMetadataWrapperImpl valueMetadata; + private boolean showMetadata; - PrismValueWrapperImpl(ItemWrapper parent, V value, ValueStatus status) { + PrismValueWrapperImpl(ItemWrapper parent, PrismValue value, ValueStatus status) { this.parent = parent; this.newValue = value; - this.oldValue = (V) value.clone(); + if (value != null) { + this.oldValue = value.clone(); + } this.status = status; } @Override - public > void addToDelta(D delta) throws SchemaException { + public > void addToDelta(D delta) throws SchemaException { switch (status) { case ADDED: if (newValue.isEmpty()) { break; } if (parent.isSingleValue()) { - delta.addValueToReplace((V) newValue.clone()); + delta.addValueToReplace(getNewValue().clone()); } else { - delta.addValueToAdd((V) newValue.clone()); + delta.addValueToAdd(getNewValue().clone()); } break; case NOT_CHANGED: @@ -64,23 +70,23 @@ public abstract class PrismValueWrapperImpl implements if (parent.isSingleValue()) { if (newValue.isEmpty()) { - delta.addValueToDelete((V) oldValue.clone()); + delta.addValueToDelete(oldValue.clone()); } else { - delta.addValueToReplace((V) newValue.clone()); + delta.addValueToReplace(newValue.clone()); } break; } if (!newValue.isEmpty()) { - delta.addValueToAdd((V) newValue.clone()); + delta.addValueToAdd(newValue.clone()); } if (!oldValue.isEmpty()) { - delta.addValueToDelete((V) oldValue.clone()); + delta.addValueToDelete(oldValue.clone()); } break; case DELETED: if (oldValue != null && !oldValue.isEmpty()) { - delta.addValueToDelete((V) oldValue.clone()); + delta.addValueToDelete(oldValue.clone()); } break; default: @@ -106,13 +112,13 @@ public T getRealValue() { @Override - public V getNewValue() { - return newValue; + public V getNewValue() { + return (V) newValue; } @Override - public V getOldValue() { - return oldValue; + public V getOldValue() { + return (V) oldValue; } @Override @@ -142,4 +148,31 @@ public String debugDump(int indent) { public boolean isVisible() { return !ValueStatus.DELETED.equals(getStatus()); } + + @Override + public void setValueMetadata(ValueMetadataWrapperImpl valueMetadata) { + this.valueMetadata = valueMetadata; + } + + @Override + public ValueMetadataWrapperImpl getValueMetadata() { + return valueMetadata; + } + + @Override + public boolean isShowMetadata() { + return showMetadata; + } + + public void setShowMetadata(boolean showMetadata) { + this.showMetadata = showMetadata; + } + + @Override + public String toShortString() { + if (getRealValue() == null) { + return ""; + } + return getRealValue().toString(); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ValueMetadataWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ValueMetadataWrapperImpl.java new file mode 100644 index 00000000000..5ac365e8f46 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ValueMetadataWrapperImpl.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.gui.impl.prism.wrapper; + +import com.evolveum.midpoint.gui.api.prism.wrapper.*; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.web.component.prism.ValueStatus; +import com.evolveum.midpoint.xml.ns._public.common.common_3.VirtualContainerItemSpecificationType; + +import java.util.List; + +public class ValueMetadataWrapperImpl implements PrismContainerValueWrapper { + + private boolean sorted; + private PrismContainerValueWrapper metadataValueWrapper; + + public ValueMetadataWrapperImpl(PrismContainerValueWrapper metadataValueWrapper) { + this.metadataValueWrapper = metadataValueWrapper; + } + + @Override + public String getDisplayName() { + if (getDefinition() == null) { + return "MetadataMock"; + } + return getDefinition().getDisplayName(); + } + + @Override + public String getHelpText() { + return metadataValueWrapper.getHelpText(); + } + + @Override + public boolean isExpanded() { + return true; + } + + @Override + public void setExpanded(boolean expanded) { + + } + + @Override + public boolean isSorted() { + return sorted; + } + + @Override + public void setSorted(boolean sorted) { + this.sorted = sorted; + } + + @Override + public List> getChildContainers() throws SchemaException { + throw new UnsupportedOperationException("Cannot create child containers for value metadata"); + } + + @Override + public Containerable getRealValue() { + return getOldValue().getRealValue(); + } + + @Override + public void setRealValue(Containerable realValue) { + throw new UnsupportedOperationException("Cannot set real value for value metadata"); + } + + @Override + public ValueStatus getStatus() { + return ValueStatus.NOT_CHANGED; + } + + @Override + public void setStatus(ValueStatus status) { + throw new UnsupportedOperationException("Cannot set value status for value metadata"); + } + + @Override + public ValueMetadata getNewValue() { + return (ValueMetadata) metadataValueWrapper.getOldValue(); + } + + @Override + public ValueMetadata getOldValue() { + return (ValueMetadata) metadataValueWrapper.getOldValue(); + } + + @Override + public IW getParent() { + return null; + } + + @Override + public > void addToDelta(D delta) throws SchemaException { + throw new UnsupportedOperationException("Cannot compute delta for valueMetadata"); + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public PrismContainerValueWrapper getValueMetadata() { + return null; + } + + @Override + public void setValueMetadata(ValueMetadataWrapperImpl valueMetadata) { + + } + + @Override + public boolean isShowMetadata() { + return false; + } + + @Override + public void setShowMetadata(boolean showMetadata) { + + } + + @Override + public List> getContainers() { + return metadataValueWrapper.getContainers(); + } + + @Override + public List> getNonContainers() { + return metadataValueWrapper.getNonContainers(); + } + + @Override + public List> getItems() { + return metadataValueWrapper.getItems(); + } + + @Override + public PrismContainerWrapper findContainer(ItemPath path) throws SchemaException { + return metadataValueWrapper.findContainer(path); + } + + @Override + public PrismPropertyWrapper findProperty(ItemPath propertyPath) throws SchemaException { + return metadataValueWrapper.findProperty(propertyPath); + } + + @Override + public PrismReferenceWrapper findReference(ItemPath path) throws SchemaException { + return metadataValueWrapper.findReference(path); + } + + @Override + public IW findItem(ItemPath path, Class type) throws SchemaException { + return metadataValueWrapper.findItem(path, type); + } + + @Override + public ItemPath getPath() { + return null; + } + + @Override + public boolean isSelected() { + return false; + } + + @Override + public boolean setSelected(boolean selected) { + return false; + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public void setReadOnly(boolean readOnly, boolean recursive) { + + } + + @Override + public boolean hasChanged() { + return false; + } + + @Override + public boolean isShowEmpty() { + return false; + } + + @Override + public void setShowEmpty(boolean setShowEmpty) { + + } + + @Override + public void applyDelta(ID delta) throws SchemaException { + + } + + @Override + public PrismContainerValue getValueToAdd() throws SchemaException { + return null; + } + + @Override + public boolean isHeterogenous() { + return false; + } + + @Override + public void setHeterogenous(boolean heterogenous) { + + } + + @Override + public void setVirtualContainerItems(List virtualItems) { + + } + + @Override + public boolean isVirtual() { + return false; + } + + @Override + public PrismContainerDefinition getDefinition() { + return metadataValueWrapper.getDefinition(); + } + + @Override + public String debugDump(int indent) { + return null; + } + + @Override + public String toShortString() { + return ""; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java index 61ef5ce6a57..1d7f1857ca1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java @@ -16,7 +16,6 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.prism.*; import org.springframework.stereotype.Component; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/TabbedPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/TabbedPanel.html index 36f1ce19d10..e79f1ccc3ab 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/TabbedPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/TabbedPanel.html @@ -7,7 +7,12 @@ --> diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java index e36665c2fe7..7671e61deef 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java @@ -366,7 +366,7 @@ protected Component createColumnPanel(String componentI if (object == null) { return new WebMarkupContainer(componentId); } - List> values = object.getValues(); + List> values = object.getValues(); List pendingOperations = new ArrayList(); values.forEach(value -> { pendingOperations.add(value.getRealValue()); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/util/ContainerListDataProvider.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/util/ContainerListDataProvider.java index 1258478e08a..b4816fe686b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/util/ContainerListDataProvider.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/util/ContainerListDataProvider.java @@ -6,8 +6,9 @@ */ package com.evolveum.midpoint.web.component.util; +import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; +import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.gui.impl.factory.wrapper.PrismContainerWrapperFactoryImpl; import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.prism.Containerable; @@ -80,11 +81,10 @@ public Iterator> internalIterator(long f LOGGER.trace("Query {} resulted in {} containers", type.getSimpleName(), list.size()); } - PrismContainerWrapperFactoryImpl containerFactory = new PrismContainerWrapperFactoryImpl(); - for (C object : list) { WrapperContext context = new WrapperContext(task, result); - getAvailableData().add(containerFactory.createContainerValueWrapper(null, object.asPrismContainerValue(), ValueStatus.NOT_CHANGED, context)); + PrismContainerWrapperFactory factory = getPage().findContainerWrapperFactory(object.asPrismContainerValue().getDefinition()); + getAvailableData().add(factory.createValueWrapper(null, object.asPrismContainerValue(), ValueStatus.NOT_CHANGED, context)); } } catch (Exception ex) { result.recordFatalError(getPage().createStringResource("ContainerListDataProvider.message.listContainers.fatalError").getString(), ex); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTask.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTask.java index fc1c8301b5e..cc65335ca9d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTask.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTask.java @@ -434,7 +434,7 @@ public void yesPerformed(AjaxRequestTarget target) { return null; } - PrismValueWrapper itemValue = item.getValue(); + PrismValueWrapper itemValue = item.getValue(); if (itemValue == null) { return null; } diff --git a/infra/axiom/src/main/antlr4/com/evolveum/axiom/lang/antlr/Axiom.g4 b/infra/axiom/src/main/antlr4/com/evolveum/axiom/lang/antlr/Axiom.g4 index bee08241596..b9a17557ece 100644 --- a/infra/axiom/src/main/antlr4/com/evolveum/axiom/lang/antlr/Axiom.g4 +++ b/infra/axiom/src/main/antlr4/com/evolveum/axiom/lang/antlr/Axiom.g4 @@ -7,39 +7,32 @@ COLON : ':'; PLUS : '+'; LINE_COMMENT : [ \n\r\t]* ('//' (~[\r\n]*)) [ \n\r\t]* -> skip; SEP: [ \n\r\t]+; -IDENTIFIER : [a-zA-Z_/][a-zA-Z0-9_\-./]*; +IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_\-]*; fragment SQOUTE : '\''; fragment DQOUTE : '"'; - -//fragment SUB_STRING : ('"' (ESC | ~["])*? '"') | ('\'' (ESC | ~['])* '\''); -//fragment ESC : '\\' (["\\/bfnrt] | UNICODE); -//fragment UNICODE : 'u' HEX HEX HEX HEX; -//fragment HEX : [0-9a-fA-F] ; -//STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'' | '}' | '/' | '+')~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '}' )* ) | SUB_STRING ); - fragment ESC : '\\'; STRING_SINGLEQUOTE: SQOUTE ((ESC SQOUTE) | ~[\n'])* SQOUTE; STRING_DOUBLEQUOTE: DQOUTE ((ESC DQOUTE) | ~[\n"])* DQOUTE; -//STRING_MULTILINE: '"""' (ESC | ~('"""'))* '"""'; -statement : SEP* identifier SEP* (argument)? SEP* (SEMICOLON | LEFT_BRACE SEP* (statement)* SEP* RIGHT_BRACE SEP*) SEP*; +//statement : SEP* identifier SEP* (argument)? SEP* (SEMICOLON | LEFT_BRACE SEP* (statement)* SEP* RIGHT_BRACE SEP*) SEP*; + + +itemBody: identifier SEP* value; +item : SEP* itemBody; +value: (argument)? SEP* (SEMICOLON | LEFT_BRACE SEP* (item | metadata)* SEP* RIGHT_BRACE SEP*) SEP*; +metadata : SEP* '@' itemBody; identifier : (prefix COLON)? localIdentifier; prefix : IDENTIFIER; localIdentifier : IDENTIFIER; - -// argument : STRING (SEP* PLUS SEP* STRING)* | IDENTIFIER; argument : identifier | string; string : singleQuoteString | doubleQuoteString | multilineString; - singleQuoteString : STRING_SINGLEQUOTE; doubleQuoteString : STRING_DOUBLEQUOTE; multilineString: '"""\n' (~('"""'))*'"""'; - - diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AbstractAxiomItem.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AbstractAxiomItem.java new file mode 100644 index 00000000000..1110b3b07fe --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AbstractAxiomItem.java @@ -0,0 +1,25 @@ +package com.evolveum.axiom.api; + +import java.util.Optional; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; + +public abstract class AbstractAxiomItem implements AxiomItem { + + + private final AxiomItemDefinition definition; + + public AbstractAxiomItem(AxiomItemDefinition definition) { + this.definition = definition; + } + + @Override + public Optional definition() { + return Optional.of(definition); + } + + @Override + public AxiomName name() { + return definition.name(); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AbstractAxiomValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AbstractAxiomValue.java new file mode 100644 index 00000000000..4135f04f928 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AbstractAxiomValue.java @@ -0,0 +1,28 @@ +package com.evolveum.axiom.api; + +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; + +public abstract class AbstractAxiomValue implements AxiomValue { + + private final AxiomTypeDefinition type; + private final Map> infraItems; + + public AbstractAxiomValue(AxiomTypeDefinition type, Map> infraItems) { + this.type = type; + this.infraItems = infraItems; + } + + @Override + public Map> infraItems() { + return infraItems; + } + + @Override + public Optional type() { + return Optional.of(type); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomComplexValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomComplexValue.java new file mode 100644 index 00000000000..af88a4e017e --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomComplexValue.java @@ -0,0 +1,52 @@ +package com.evolveum.axiom.api; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; + +public interface AxiomComplexValue extends AxiomValue>> { + + + @Override + default Collection> value() { + return itemMap().values(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + default Optional> item(AxiomItemDefinition def) { + return (Optional) item(def.name()); + } + + @SuppressWarnings("unchecked") + default Optional> item(AxiomName name) { + return Optional.ofNullable((AxiomItem) itemMap().get(name)); + } + + default Optional> onlyValue(Class type, AxiomItemDefinition... components) { + Optional> current = Optional.of(this); + for(AxiomItemDefinition name : components) { + current = current.get().asComplex().flatMap(c -> c.item(name)).map(i -> i.onlyValue()); + if(!current.isPresent()) { + return Optional.empty(); + } + } + return (Optional) current; + } + + Map> itemMap(); + + + interface Factory extends AxiomValueFactory>> { + + @Override + default AxiomValue>> createSimple(AxiomTypeDefinition def, + Collection> value, Map> infraItems) { + throw new IllegalStateException("Factory is only for complex types"); + } + + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomInfraValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomInfraValue.java new file mode 100644 index 00000000000..c8601c377dd --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomInfraValue.java @@ -0,0 +1,17 @@ +package com.evolveum.axiom.api; + +import java.util.Map; +import java.util.Optional; + +public interface AxiomInfraValue { + + Map> infraItems(); + + default Optional> infraItem(AxiomName name) { + return Optional.ofNullable(infraItems().get(name)); + } + + interface Factory { + V create(Map> infraItems); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItem.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItem.java new file mode 100644 index 00000000000..b7a72318d7a --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItem.java @@ -0,0 +1,30 @@ +package com.evolveum.axiom.api; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.google.common.collect.Iterables; + +public interface AxiomItem { + + AxiomName name(); + Optional definition(); + + Collection> values(); + + default AxiomValue onlyValue() { + return Iterables.getOnlyElement(values()); + } + + static AxiomItem from(AxiomItemDefinition def, Collection> values) { + return AxiomItemImpl.from(def, values); + } + + static AxiomItem from(AxiomItemDefinition def, AxiomValue value) { + return AxiomItemImpl.from(def, Collections.singleton(value)); + } + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemBuilder.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemBuilder.java new file mode 100644 index 00000000000..794585791a4 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemBuilder.java @@ -0,0 +1,43 @@ +package com.evolveum.axiom.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.ImmutableList.Builder; + +public class AxiomItemBuilder implements Supplier> { + + Collection>> values = new ArrayList<>(); + private AxiomItemDefinition definition; + + public AxiomItemBuilder(AxiomItemDefinition definition) { + this.definition = definition; + } + + public AxiomItemDefinition definition() { + return definition; + } + + public void addValue(Supplier> value) { + values.add(value); + } + + @Override + public AxiomItem get() { + Builder> result = ImmutableList.builder(); + for(Supplier> value : values) { + result.add(value.get()); + } + return AxiomItem.from(definition, result.build()); + } + + public Supplier> onlyValue() { + return Iterables.getOnlyElement(values); + } + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemFactory.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemFactory.java new file mode 100644 index 00000000000..7947e8c40e1 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemFactory.java @@ -0,0 +1,11 @@ +package com.evolveum.axiom.api; + +import java.util.Collection; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; + +public interface AxiomItemFactory { + + AxiomItem create(AxiomItemDefinition def, Collection> axiomItem); + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemImpl.java new file mode 100644 index 00000000000..eceb2f34e4e --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomItemImpl.java @@ -0,0 +1,26 @@ +package com.evolveum.axiom.api; + +import java.util.Collection; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.google.common.collect.ImmutableList; + +class AxiomItemImpl extends AbstractAxiomItem { + + private final Collection> values; + + private AxiomItemImpl(AxiomItemDefinition definition, Collection> val) { + super(definition); + this.values = ImmutableList.copyOf(val); + } + + static AxiomItem from(AxiomItemDefinition definition, Collection> values) { + return new AxiomItemImpl<>(definition, values); + } + + @Override + public Collection> values() { + return values; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomIdentifier.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomName.java similarity index 52% rename from infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomIdentifier.java rename to infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomName.java index 8af5ae6caba..7f4b56d5b41 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomIdentifier.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomName.java @@ -6,26 +6,27 @@ */ package com.evolveum.axiom.api; -import java.util.Objects; +import org.jetbrains.annotations.NotNull; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; -public class AxiomIdentifier { +public class AxiomName { - public static final String AXIOM_NAMESPACE = "https://ns.evolveum.com/axiom"; + public static final String AXIOM_NAMESPACE = "https://schema.evolveum.com/ns/axiom/model"; private final String namespace; private final String localName; - public AxiomIdentifier(String namespace, String localName) { + public AxiomName(String namespace, String localName) { this.namespace = Preconditions.checkNotNull(namespace, "namespace"); this.localName = Preconditions.checkNotNull(localName, "localName"); } - public String getNamespace() { + public String namespace() { return namespace; } - public String getLocalName() { + public String localName() { return localName; } @@ -42,9 +43,9 @@ public int hashCode() { public boolean equals(Object obj) { if (this == obj) return true; - if (!(obj instanceof AxiomIdentifier)) + if (!(obj instanceof AxiomName)) return false; - AxiomIdentifier other = (AxiomIdentifier) obj; + AxiomName other = (AxiomName) obj; if (localName == null) { if (other.localName != null) return false; @@ -60,15 +61,38 @@ public boolean equals(Object obj) { @Override public String toString() { - return localName; + if(Strings.isNullOrEmpty(namespace)) { + return localName; + } + return "(" + namespace + ")" +localName; + } + + public static AxiomName axiom(String identifier) { + return new AxiomName(AXIOM_NAMESPACE, identifier); + } + + public static AxiomName from(String namespace, String localName) { + return new AxiomName(namespace, localName); + } + + public boolean sameNamespace(AxiomName other) { + return this.namespace().equals(other.namespace()); + } + + public AxiomName localName(String name) { + return AxiomName.from(namespace, name); + } + + public AxiomName namespace(String targetNamespace) { + return AxiomName.from(targetNamespace, localName); } - public static AxiomIdentifier axiom(String identifier) { - return new AxiomIdentifier(AXIOM_NAMESPACE, identifier); + public AxiomName defaultNamespace() { + return AxiomName.from("", localName); } - public static AxiomIdentifier from(String namespace, String localName) { - return new AxiomIdentifier(namespace, localName); + public static AxiomName local(@NotNull String localName) { + return from("", localName); } } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomSimpleValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomSimpleValue.java new file mode 100644 index 00000000000..662b4090542 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomSimpleValue.java @@ -0,0 +1,9 @@ +package com.evolveum.axiom.api; + +import java.util.Optional; + +public interface AxiomSimpleValue extends AxiomValue { + + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValue.java new file mode 100644 index 00000000000..31a29a3aded --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValue.java @@ -0,0 +1,38 @@ +package com.evolveum.axiom.api; + +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; + + +public interface AxiomValue extends AxiomInfraValue { + + AxiomName TYPE = AxiomName.axiom("type"); + AxiomName VALUE = AxiomName.axiom("value"); + + Optional type(); + + V value(); + + default Optional asComplex() { + if(this instanceof AxiomComplexValue) { + return Optional.of((AxiomComplexValue) this); + } + return Optional.empty(); + } + + interface Factory> extends AxiomInfraValue.Factory { + + @Override + default T create(Map> infraItems) { + AxiomTypeDefinition type = (AxiomTypeDefinition) infraItems.get(TYPE).onlyValue().value(); + V value = (V) infraItems.get(VALUE).onlyValue().value(); + return create(type, value, infraItems); + } + + T create(AxiomTypeDefinition type, V value, Map> infraItems); + + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValueBuilder.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValueBuilder.java new file mode 100644 index 00000000000..6406cbda8d8 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValueBuilder.java @@ -0,0 +1,93 @@ +package com.evolveum.axiom.api; + +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.concepts.Lazy; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; + +public class AxiomValueBuilder implements Lazy.Supplier> { + + private AxiomTypeDefinition type; + + private AxiomValueFactory factory; + private Map>> children = new LinkedHashMap<>(); + private Map>> infra = new LinkedHashMap<>(); + private V value; + + public AxiomValueBuilder(AxiomTypeDefinition type, AxiomValueFactory factory) { + this.type = type; + this.factory = factory; + } + + public static AxiomValueBuilder from(AxiomTypeDefinition type) { + return new AxiomValueBuilder<>(type, AxiomValueFactory.defaultFactory()); + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + + public void add(AxiomName name, Supplier> child) { + children.put(name, child); + } + + public Supplier> get(AxiomName name) { + return children.get(name); + } + + public Supplier> get(AxiomName name, Function>> child) { + return children.computeIfAbsent(name, child); + } + + public Supplier> getInfra(AxiomName name, Function>> child) { + return infra.computeIfAbsent(name, child); + } + + @Override + public AxiomValue get() { + if(type.isComplex()) { + Builder> builder = ImmutableMap.builder(); + return (AxiomValue) factory.createComplex(type, build(children), build(infra)); + } + Preconditions.checkState(children.isEmpty(), "%s does not have items. Items found %s", type.name(), children.keySet()); + return factory.createSimple(type, value, Collections.emptyMap()); + } + + private static Map> build(Map>> children) { + Builder> builder = ImmutableMap.builder(); + for(Entry>> entry : children.entrySet()) { + builder.put(entry.getKey(), entry.getValue().get()); + } + return builder.build(); + } + + public static AxiomValueBuilder create(AxiomTypeDefinition type, AxiomValueFactory factory) { + return new AxiomValueBuilder<>(type, factory); + } + + public void setFactory(AxiomValueFactory factoryFor) { + this.factory = factoryFor; + } + + public AxiomTypeDefinition type() { + return type; + } + + public void setType(AxiomTypeDefinition type) { + this.type = type; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValueFactory.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValueFactory.java new file mode 100644 index 00000000000..aa22dde8957 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/AxiomValueFactory.java @@ -0,0 +1,33 @@ +package com.evolveum.axiom.api; + +import java.util.Map; + +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; + +public interface AxiomValueFactory { + + AxiomValueFactory DEFAULT_FACTORY = new AxiomValueFactory<>() { + + @Override + public AxiomComplexValue createComplex(AxiomTypeDefinition def, Map> items, + Map> infraItems) { + return new ComplexValueImpl(def, items, infraItems); + } + + @Override + public AxiomValue createSimple(AxiomTypeDefinition def, Object value, + Map> infraItems) { + return new SimpleValue(def, value, infraItems); + } + }; + + AxiomValue createSimple(AxiomTypeDefinition def, V value, Map> infraItems); + + AxiomComplexValue createComplex(AxiomTypeDefinition def, Map> items ,Map> infraItems); + + @SuppressWarnings("unchecked") + static AxiomValueFactory defaultFactory() { + return (AxiomValueFactory) DEFAULT_FACTORY; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/ComplexValueImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/ComplexValueImpl.java new file mode 100644 index 00000000000..5f1a9400cc8 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/ComplexValueImpl.java @@ -0,0 +1,58 @@ +package com.evolveum.axiom.api; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.google.common.collect.ImmutableMap; + +public class ComplexValueImpl implements AxiomComplexValue { + + private final AxiomTypeDefinition type; + private final Map> items; + private final Map> infraItems; + + + + protected X require(Optional value) { + return value.get(); + } + + public ComplexValueImpl(AxiomTypeDefinition type, Map> items, Map> infraItems) { + super(); + this.type = type; + this.items = ImmutableMap.copyOf(items); + this.infraItems = ImmutableMap.copyOf(infraItems); + } + + @Override + public Optional type() { + return Optional.ofNullable(type); + } + + @Override + public Optional> item(AxiomItemDefinition def) { + return AxiomComplexValue.super.item(def); + } + + @Override + public Optional> item(AxiomName name) { + return Optional.ofNullable((AxiomItem) items.get(name)); + } + + public Collection> items() { + return items.values(); + } + + @Override + public Map> itemMap() { + return items; + } + + @Override + public Map> infraItems() { + return infraItems; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/SimpleValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/SimpleValue.java new file mode 100644 index 00000000000..9bb79121cf9 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/SimpleValue.java @@ -0,0 +1,24 @@ +package com.evolveum.axiom.api; + +import java.util.Map; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; + +public class SimpleValue extends AbstractAxiomValue implements AxiomSimpleValue { + + private final T value; + + SimpleValue(AxiomTypeDefinition type, T value, Map> infraItems) { + super(type, infraItems); + this.value = value; + } + + public static final AxiomSimpleValue create(AxiomTypeDefinition def, V value, Map> infraItems) { + return new SimpleValue(def, value, infraItems); + } + + @Override + public T value() { + return value; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/meta/Inheritance.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/meta/Inheritance.java new file mode 100644 index 00000000000..8d203a02896 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/meta/Inheritance.java @@ -0,0 +1,35 @@ +package com.evolveum.axiom.api.meta; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; + +public interface Inheritance { + + Inheritance INHERIT = Inheritance::inheritNamespace; + Inheritance NO_CHANGE = Inheritance::noChange; + Inheritance NO_NAMESPACE = Inheritance::noNamespace; + Inheritance CURRENT = NO_CHANGE; + + + static AxiomName adapt(AxiomName parent, AxiomName child) { + return CURRENT.apply(parent, child); + } + + static AxiomName adapt(AxiomName parent, AxiomItemDefinition child) { + return child.inherited() ? adapt(parent, child.name()) : child.name(); + } + + AxiomName apply(AxiomName parent, AxiomName child); + + static AxiomName inheritNamespace(AxiomName parent, AxiomName name) { + return parent.localName(name.localName()); + } + + static AxiomName noNamespace(AxiomName parent, AxiomName name) { + return name.defaultNamespace(); + } + + static AxiomName noChange(AxiomName parent, AxiomName name) { + return name; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomIdentifierDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomIdentifierDefinition.java new file mode 100644 index 00000000000..36430bd56c1 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomIdentifierDefinition.java @@ -0,0 +1,50 @@ +package com.evolveum.axiom.api.schema; + +import java.util.Collection; +import java.util.Set; + +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomValue; +import com.google.common.collect.ImmutableSet; + +public interface AxiomIdentifierDefinition extends AxiomComplexValue { + + Collection components(); + + enum Scope { + GLOBAL, + PARENT, + LOCAL + } + + static AxiomIdentifierDefinition global(AxiomName name, AxiomName... components) { + return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(components), name, Scope.GLOBAL); + } + + static AxiomIdentifierDefinition local(AxiomName name, AxiomName... components) { + return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(components), name, Scope.LOCAL); + } + + static Scope scope(String scope) { + if(Scope.GLOBAL.name().equalsIgnoreCase(scope)) { + return Scope.GLOBAL; + } + if(Scope.PARENT.name().equalsIgnoreCase(scope)) { + return Scope.PARENT; + } + if(Scope.LOCAL.name().equalsIgnoreCase(scope)) { + return Scope.LOCAL; + } + throw new IllegalArgumentException("Unknown scope " + scope); + } + + static AxiomIdentifierDefinition from(AxiomName space, Scope scope, Set members) { + return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(members), space, scope); + } + + static AxiomIdentifierDefinition parent(AxiomName name, AxiomName... components) { + return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(components), name, Scope.PARENT); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomIdentifierDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomIdentifierDefinitionImpl.java new file mode 100644 index 00000000000..d341ca09ff0 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomIdentifierDefinitionImpl.java @@ -0,0 +1,40 @@ +package com.evolveum.axiom.api.schema; + +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomName; +import com.google.common.collect.ImmutableSet; + +class AxiomIdentifierDefinitionImpl implements AxiomIdentifierDefinition { + + private Set components; + + + public AxiomIdentifierDefinitionImpl(Set components, AxiomName space, Scope scope) { + super(); + this.components = ImmutableSet.copyOf(components); + } + + @Override + public Set components() { + return components; + } + @Override + public Optional type() { + return null; + } + + @Override + public Map> itemMap() { + return Collections.emptyMap(); + } + + @Override + public Map> infraItems() { + return null; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomItemDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomItemDefinition.java new file mode 100644 index 00000000000..20b5eff2277 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomItemDefinition.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.api.schema; + +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; + +public interface AxiomItemDefinition extends AxiomNamedDefinition, AxiomComplexValue { + + AxiomName ROOT_SPACE = AxiomName.axiom("AxiomRootDefinition"); + AxiomName SPACE = AxiomName.axiom("AxiomItemDefinition"); + AxiomName NAME = AxiomName.axiom("name"); + AxiomName VALUE_SPACE = AxiomName.axiom("value"); + + AxiomTypeDefinition typeDefinition(); + + boolean operational(); + + default boolean inherited() { + return true; + } + + default boolean required() { + return minOccurs() > 0; + } + + AxiomTypeDefinition definingType(); + + int minOccurs(); + int maxOccurs(); + + static String toString(AxiomItemDefinition def) { + return MoreObjects.toStringHelper(AxiomItemDefinition.class) + .add("name", def.name()) + .add("type", def.type()) + .toString(); + } + + static AxiomItemDefinition derived(AxiomName name , AxiomItemDefinition source) { + return new DelegatedItemDefinition() { + + @Override + protected AxiomItemDefinition delegate() { + return source; + } + + @Override + public AxiomName name() { + return name; + } + }; + } + + static IdentifierSpaceKey identifier(AxiomName name) { + return IdentifierSpaceKey.from(ImmutableMap.of(NAME, name)); + } + + interface Inherited extends AxiomItemDefinition { + + AxiomItemDefinition original(); + + } + + interface Extended extends AxiomItemDefinition { + + AxiomItemDefinition original(); + + } + + default AxiomItemDefinition derived(AxiomName name) { + return derived(name, this); + } + + default AxiomItemDefinition notInherited() { + return new DelegatedItemDefinition() { + + @Override + public boolean operational() { + return false; + } + + @Override + public boolean inherited() { + return false; + } + + @Override + protected AxiomItemDefinition delegate() { + return AxiomItemDefinition.this; + } + }; + } + + Optional identifierDefinition(); +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBaseDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomNamedDefinition.java similarity index 59% rename from infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBaseDefinition.java rename to infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomNamedDefinition.java index 628f31b3ec4..23e07296f81 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomBaseDefinition.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomNamedDefinition.java @@ -4,12 +4,12 @@ * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.axiom.lang.api; +package com.evolveum.axiom.api.schema; -import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.api.AxiomName; -public interface AxiomBaseDefinition { +public interface AxiomNamedDefinition { - AxiomIdentifier name(); + AxiomName name(); String documentation(); } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomSchemaContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomSchemaContext.java new file mode 100644 index 00000000000..6a0605bfd8f --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomSchemaContext.java @@ -0,0 +1,19 @@ +package com.evolveum.axiom.api.schema; + +import java.util.Collection; +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomName; + +public interface AxiomSchemaContext { + + Collection roots(); + + Optional getRoot(AxiomName type); + + Optional getType(AxiomName type); + + Collection types(); + + //AxiomValueFactory factoryFor(AxiomTypeDefinition type); +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomTypeDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomTypeDefinition.java new file mode 100644 index 00000000000..8a4c9b4957f --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/AxiomTypeDefinition.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.api.schema; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.meta.Inheritance; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.google.common.collect.ImmutableMap; + +public interface AxiomTypeDefinition extends AxiomNamedDefinition, AxiomComplexValue { + + public final AxiomName IDENTIFIER_MEMBER = AxiomName.axiom("name"); + public final AxiomName SPACE = AxiomName.axiom("AxiomTypeDefinition"); + public final AxiomName NAME = AxiomName.axiom("name"); + + + + @Override + default Optional type() { + return Optional.empty(); + } + + Optional argument(); + + Optional superType(); + + Map itemDefinitions(); + + Collection identifierDefinitions(); + + default Optional itemDefinition(AxiomName child) { + AxiomItemDefinition maybe = itemDefinitions().get(child); + if(maybe == null) { + maybe = itemDefinitions().get(Inheritance.adapt(name(), child)); + } + if(maybe == null && child.namespace().isEmpty()) { + maybe = itemDefinitions().get(name().localName(child.localName())); + } + if(maybe != null) { + return Optional.of(maybe); + } + return superType().flatMap(s -> s.itemDefinition(child)); + } + + static IdentifierSpaceKey identifier(AxiomName name) { + return IdentifierSpaceKey.from(ImmutableMap.of(IDENTIFIER_MEMBER, name)); + } + + default Collection requiredItems() { + return itemDefinitions().values().stream().filter(AxiomItemDefinition::required).collect(Collectors.toList()); + } + + default Optional itemDefinition(AxiomName parentItem, AxiomName name) { + return itemDefinition(Inheritance.adapt(parentItem, name)); + } + + default boolean isSubtypeOf(AxiomTypeDefinition type) { + return isSubtypeOf(type.name()); + } + + default boolean isSupertypeOf(AxiomTypeDefinition other) { + return other.isSubtypeOf(this); + } + + default boolean isSubtypeOf(AxiomName other) { + Optional current = Optional.of(this); + while(current.isPresent()) { + if(current.get().name().equals(other)) { + return true; + } + current = current.get().superType(); + } + return false; + } + + default boolean isComplex() { + return !itemDefinitions().isEmpty(); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/DelegatedItemDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/DelegatedItemDefinition.java new file mode 100644 index 00000000000..f0e101a75a3 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/schema/DelegatedItemDefinition.java @@ -0,0 +1,87 @@ +package com.evolveum.axiom.api.schema; + +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; + +abstract class DelegatedItemDefinition implements AxiomItemDefinition { + + protected abstract AxiomItemDefinition delegate(); + + @Override + public boolean operational() { + return false; + } + + @Override + public Optional type() { + return delegate().type(); + } + + @Override + public Map> itemMap() { + return delegate().itemMap(); + } + + @Override + public AxiomName name() { + return delegate().name(); + } + + @Override + public String documentation() { + return delegate().documentation(); + } + + @Override + public Optional> item(AxiomItemDefinition def) { + return delegate().asComplex().get().item(def); + } + + @Override + public Optional> item(AxiomName name) { + return delegate().asComplex().get().item(name); + } + + @Override + public AxiomTypeDefinition typeDefinition() { + return delegate().typeDefinition(); + } + + @Override + public boolean required() { + return delegate().required(); + } + + @Override + public int minOccurs() { + return delegate().minOccurs(); + } + + @Override + public int maxOccurs() { + return delegate().maxOccurs(); + } + + @Override + public String toString() { + return AxiomItemDefinition.toString(this); + } + + @Override + public AxiomTypeDefinition definingType() { + return delegate().definingType(); + } + + @Override + public Optional identifierDefinition() { + return delegate().identifierDefinition(); + } + + @Override + public Map> infraItems() { + return delegate().infraItems(); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomBuilderStreamTarget.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomBuilderStreamTarget.java new file mode 100644 index 00000000000..a89f3e425c7 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomBuilderStreamTarget.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.api.stream; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.Optional; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + + +public class AxiomBuilderStreamTarget implements AxiomItemStream.TargetWithResolver { + + private final Deque queue = new LinkedList<>(); + + protected AxiomBuilderStreamTarget() {} + + public AxiomBuilderStreamTarget(ValueBuilder root) { + queue.add(root); + } + + protected V offer(V builder) { + queue.offerFirst(builder); + return builder; + } + + protected Builder current() { + return queue.peek(); + } + + protected Builder poll() { + return queue.poll(); + } + + private ItemBuilder item(Builder node) { + Preconditions.checkState(node instanceof ItemBuilder, "Incorrect nesting: expected item, got value"); + return (ItemBuilder) node; + } + + private ValueBuilder value(Builder node) { + Preconditions.checkState(node instanceof ValueBuilder, "Incorrect nesting: expected value, got item"); + return (ValueBuilder) node; + } + + @Override + public void startValue(Object value, SourceLocation loc) { + queue.offerFirst(item(current()).startValue(value, loc)); + } + + @Override + public void endValue(SourceLocation loc) { + value(poll()).endValue(loc); + } + + @Override + public void startItem(AxiomName item, SourceLocation loc) { + Optional childDef = value(current()).childItemDef(item); + AxiomSyntaxException.check(childDef.isPresent(), loc , "Item %s not allowed in %s", item, current().name()); + offer(value(current()).startItem(item, loc)); + } + + @Override + public void startInfra(AxiomName item, SourceLocation loc) { + Optional childDef = value(current()).infraItemDef(item); + AxiomSyntaxException.check(childDef.isPresent(), loc , "Infra Item %s not allowed in %s", item, current().name()); + offer(value(current()).startInfra(item, loc)); + } + + @Override + public void endInfra(SourceLocation loc) { + item(poll()).endNode(loc); + } + + @Override + public void endItem(SourceLocation loc) { + item(poll()).endNode(loc); + } + + private interface Builder { + AxiomName name(); + + AxiomNameResolver itemResolver(); + + AxiomNameResolver valueResolver(); + } + + public interface ItemBuilder extends Builder { + ValueBuilder startValue(Object value, SourceLocation loc); + void endNode(SourceLocation loc); + + } + + public interface ValueBuilder extends Builder { + Optional childItemDef(AxiomName statement); + Optional infraItemDef(AxiomName item); + ItemBuilder startItem(AxiomName identifier, SourceLocation loc); + ItemBuilder startInfra(AxiomName identifier, SourceLocation loc); + void endValue(SourceLocation loc); + + default AxiomNameResolver axiomAsConditionalDefault() { + return (prefix, name) -> { + if(Strings.isNullOrEmpty(prefix)) { + AxiomName axiomNs = AxiomName.axiom(name); + if(childItemDef(axiomNs).isPresent()) { + return axiomNs; + } + } + return null; + }; + } + } + + @Override + public AxiomNameResolver itemResolver() { + return current().itemResolver(); + } + + @Override + public AxiomNameResolver valueResolver() { + return current().valueResolver(); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomItemStream.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomItemStream.java new file mode 100644 index 00000000000..2da9fcb9ed0 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomItemStream.java @@ -0,0 +1,28 @@ +package com.evolveum.axiom.api.stream; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; + +public interface AxiomItemStream { + + interface Target { + void startItem(AxiomName item, SourceLocation loc); + void endItem(SourceLocation loc); + + void startValue(Object value, SourceLocation loc); + void endValue(SourceLocation loc); + + default void startInfra(AxiomName item, SourceLocation loc) {}; + default void endInfra(SourceLocation loc) {}; + + } + + interface TargetWithResolver extends Target { + + AxiomNameResolver itemResolver(); + AxiomNameResolver valueResolver(); + + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomItemTarget.java b/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomItemTarget.java new file mode 100644 index 00000000000..1dabfbf66a9 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/api/stream/AxiomItemTarget.java @@ -0,0 +1,286 @@ +package com.evolveum.axiom.api.stream; + +import java.util.Optional; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomItemBuilder; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.AxiomValueBuilder; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.api.AxiomBuiltIn; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.google.common.base.Preconditions; + +public class AxiomItemTarget extends AxiomBuilderStreamTarget implements Supplier>, AxiomItemStream.TargetWithResolver { + + private final AxiomSchemaContext context; + private final AxiomNameResolver resolver; + private AxiomTypeDefinition infraType = AxiomBuiltIn.Type.AXIOM_VALUE; + private Item result; + + public AxiomItemTarget(AxiomSchemaContext context) { + this(context, AxiomNameResolver.nullResolver()); + } + + public AxiomItemTarget(AxiomSchemaContext context, AxiomNameResolver rootResolver) { + offer(new Root()); + this.context = context; + this.resolver = Preconditions.checkNotNull(rootResolver, "rootResolver"); + } + + @Override + public AxiomItem get() { + return result.get(); + } + + private final class Root implements ValueBuilder { + + @Override + public AxiomName name() { + return AxiomName.axiom("AbstractRoot"); + } + + @Override + public AxiomNameResolver itemResolver() { + return axiomAsConditionalDefault().or(resolver); + } + + @Override + public AxiomNameResolver valueResolver() { + return resolver; + } + + @Override + public Optional childItemDef(AxiomName statement) { + return context.getRoot(statement); + } + + @Override + public Optional infraItemDef(AxiomName item) { + return infraType.itemDefinition(item); + } + + @Override + public ItemBuilder startItem(AxiomName name, SourceLocation loc) { + result = new Item<>(childItemDef(name).get()); + return result; + } + + @Override + public void endValue(SourceLocation loc) { + + } + + @Override + public ItemBuilder startInfra(AxiomName name, SourceLocation loc) { + // TODO Auto-generated method stub + return null; + } + + } + + private class Item implements ItemBuilder, Supplier> { + + protected final AxiomItemBuilder builder; + + public Item(AxiomItemDefinition definition) { + this.builder = new AxiomItemBuilder<>(definition); + } + + @Override + public AxiomName name() { + return builder.definition().name(); + } + + @Override + public AxiomNameResolver itemResolver() { + return resolver; + } + + @Override + public AxiomNameResolver valueResolver() { + return resolver; + } + + + protected Value onlyValue() { + return (Value) builder.onlyValue(); + } + + @Override + public ValueBuilder startValue(Object value, SourceLocation loc) { + Value newValue = new Value<>((V) value, builder.definition().typeDefinition()); + builder.addValue(newValue); + return newValue; + } + + @Override + public void endNode(SourceLocation loc) { + // Noop for now + } + + @Override + public AxiomItem get() { + return builder.get(); + } + + } + + private final class ValueItem implements ItemBuilder, Supplier> { + + private Value value; + + public ValueItem(Value value) { + this.value = value; + } + + @Override + public AxiomName name() { + return AxiomValue.VALUE; + } + + @Override + public AxiomNameResolver itemResolver() { + return resolver; + } + + @Override + public AxiomNameResolver valueResolver() { + return resolver; + } + + @Override + public ValueBuilder startValue(Object value, SourceLocation loc) { + this.value.setValue((V) value); + return this.value; + } + + + @Override + public void endNode(SourceLocation loc) { + // Noop for now + } + + @Override + public AxiomItem get() { + throw new UnsupportedOperationException("Should not be called"); + } + } + + private final class TypeItem extends Item { + + private Value value; + + public TypeItem(AxiomItemDefinition definition) { + super(definition); + } + + public TypeItem(Value value, AxiomItemDefinition definition) { + super(definition); + this.value = value; + } + + @Override + public void endNode(SourceLocation loc) { + AxiomName typeName = (AxiomName) onlyValue().get().asComplex().get().item(AxiomTypeDefinition.NAME).get().onlyValue().value(); + Optional typeDef = context.getType(typeName); + AxiomSemanticException.check(typeDef.isPresent(), loc, "% type is not defined.", typeName); + this.value.setType(typeDef.get(),loc); + super.endNode(loc); + } + } + + + private final class Value implements ValueBuilder, Supplier> { + + private final AxiomValueBuilder builder; + private AxiomTypeDefinition type; + + public Value(V value, AxiomTypeDefinition type) { + this.type = type; + builder = AxiomValueBuilder.from(type); + if(value != null) { + setValue(value); + } + + } + + public void setType(AxiomTypeDefinition type, SourceLocation start) { + AxiomSemanticException.check(type.isSubtypeOf(this.type), start, "%s is not subtype of %s", type.name(), this.type.name()); + this.type = type; + builder.setType(type); + } + + void setValue(V value) { + if(type.argument().isPresent()) { + startItem(type.argument().get().name(), null).startValue(value, null); + } else { + builder.setValue(value); + } + + } + + @Override + public AxiomName name() { + return builder.type().name(); + } + + @Override + public AxiomNameResolver itemResolver() { + return AxiomNameResolver.defaultNamespaceFromType(builder.type()); + } + + @Override + public AxiomNameResolver valueResolver() { + return resolver; + } + + @Override + public Optional childItemDef(AxiomName statement) { + return builder.type().itemDefinition(statement); + } + + @Override + public Optional infraItemDef(AxiomName item) { + return infraType.itemDefinition(item); + } + + @Override + public ItemBuilder startItem(AxiomName name, SourceLocation loc) { + Object itemImpl = builder.get(name, (id) -> { + return new Item(childItemDef(name).get()); + }); + return (Item) (itemImpl); + } + + @Override + public void endValue(SourceLocation loc) { + // Noop for now + } + + @Override + public AxiomValue get() { + return builder.get(); + } + + @Override + public ItemBuilder startInfra(AxiomName name, SourceLocation loc) { + if(AxiomValue.VALUE.equals(name)) { + return new ValueItem(this); + } else if (AxiomValue.TYPE.equals(name)) { + return new TypeItem(this, infraItemDef(name).get()); + } + Supplier> itemImpl = builder.getInfra(name, (id) -> { + return new Item<>(infraItemDef(name).get()); + }); + return (Item) itemImpl; + } + + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java new file mode 100644 index 00000000000..92296f5b2aa --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/AbstractLazy.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.concepts; + +public abstract class AbstractLazy { + + private Object value; + + AbstractLazy(Object supplier) { + value = supplier; + } + + T unwrap() { + if(value instanceof Lazy.Supplier) { + value = ((Lazy.Supplier) value).get(); + return unwrap(); + } + return (T) value; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/Identifiable.java b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/Identifiable.java similarity index 95% rename from infra/axiom/src/main/java/com/evolveum/axiom/lang/api/Identifiable.java rename to infra/axiom/src/main/java/com/evolveum/axiom/concepts/Identifiable.java index a385213325d..4ee4d2abf31 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/Identifiable.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/Identifiable.java @@ -1,4 +1,4 @@ -package com.evolveum.axiom.lang.api; +package com.evolveum.axiom.concepts; import java.util.Map; diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/concepts/Lazy.java b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/Lazy.java index 4f97b099b4a..5b65bc33ff5 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/concepts/Lazy.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/Lazy.java @@ -6,20 +6,18 @@ */ package com.evolveum.axiom.concepts; -public class Lazy implements java.util.function.Supplier { +public class Lazy extends AbstractLazy implements java.util.function.Supplier { private static final Lazy NULL = Lazy.instant(null); - private Object value; private Lazy(Object supplier) { - value = supplier; + super(supplier); } public static final Lazy from(Supplier supplier) { return new Lazy<>(supplier); } - public static Lazy instant(T value) { return new Lazy(value); } @@ -29,13 +27,9 @@ public static Lazy nullValue() { return NULL; } - @SuppressWarnings("unchecked") @Override public T get() { - if(value instanceof Supplier) { - value = ((Supplier) value).get(); - } - return (T) value; + return unwrap(); } public interface Supplier extends java.util.function.Supplier { diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/concepts/LazyDelegate.java b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/LazyDelegate.java new file mode 100644 index 00000000000..f8838762377 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/LazyDelegate.java @@ -0,0 +1,12 @@ +package com.evolveum.axiom.concepts; + +public abstract class LazyDelegate extends AbstractLazy { + + public LazyDelegate(Lazy.Supplier supplier) { + super(supplier); + } + + protected T delegate() { + return unwrap(); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/SourceLocation.java b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/SourceLocation.java similarity index 83% rename from infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/SourceLocation.java rename to infra/axiom/src/main/java/com/evolveum/axiom/concepts/SourceLocation.java index 8164b9f3af4..593e423101a 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/SourceLocation.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/concepts/SourceLocation.java @@ -1,4 +1,4 @@ -package com.evolveum.axiom.lang.api.stmt; +package com.evolveum.axiom.concepts; public class SourceLocation { @@ -16,9 +16,6 @@ public static SourceLocation from(String source, int line, int pos) { return new SourceLocation(source, line, pos); } - - - @Override public String toString() { return sourceName + "["+ line + ":" + character + "]"; @@ -36,6 +33,8 @@ public int getChar() { return character; } - + public static SourceLocation runtime() { + return SourceLocation.from("IN-MEMORY", 0, 0); + } } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AbstractAxiomAntlrVisitor.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AbstractAxiomAntlrVisitor.java new file mode 100644 index 00000000000..f90fee656b2 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AbstractAxiomAntlrVisitor.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.antlr; + +import java.util.Optional; +import java.util.Set; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.Token; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.stream.AxiomItemStream; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.antlr.AxiomParser.ArgumentContext; +import com.evolveum.axiom.lang.antlr.AxiomParser.IdentifierContext; +import com.evolveum.axiom.lang.antlr.AxiomParser.ItemBodyContext; +import com.evolveum.axiom.lang.antlr.AxiomParser.ItemContext; +import com.evolveum.axiom.lang.antlr.AxiomParser.MetadataContext; +import com.evolveum.axiom.lang.antlr.AxiomParser.StringContext; + +public abstract class AbstractAxiomAntlrVisitor extends AxiomBaseVisitor { + + private final Optional> limit; + private final String sourceName; + + private interface StartDelegate { + void start(AxiomName identifier, SourceLocation location); + } + private interface EndDelegate { + void end(SourceLocation location); + } + + + public AbstractAxiomAntlrVisitor(String name, Set limit) { + this.sourceName = name; + this.limit = Optional.ofNullable(limit); + } + + private AxiomName statementIdentifier(IdentifierContext identifier) { + String prefix = nullableText(identifier.prefix()); + String localName = identifier.localIdentifier().getText(); + return resolveItemName(prefix, localName); + } + + + protected abstract AxiomItemStream.Target delegate(); + protected abstract AxiomName resolveItemName(String prefix, String localName); + protected abstract AxiomName resolveArgument(String prefix, String localName); + + private String nullableText(ParserRuleContext prefix) { + return prefix != null ? prefix.getText() : ""; + } + + + @Override + public T visitItem(ItemContext ctx) { + AxiomName identifier = statementIdentifier(ctx.itemBody().identifier()); + return processItemBody(identifier, ctx.itemBody(), delegate()::startItem, delegate()::endItem); + } + + public T processItemBody(AxiomName identifier, ItemBodyContext ctx, StartDelegate start, EndDelegate end) { + if(canEmit(identifier)) { + + SourceLocation startLoc = sourceLocation(ctx.identifier().start); + start.start(identifier, startLoc); + + ArgumentContext argument = ctx.value().argument(); + final Object value; + final SourceLocation valueStart; + + if(argument != null) { + value = convert(argument); + valueStart = sourceLocation(argument.start); + } else { + value = null; + valueStart = startLoc; + } + + delegate().startValue(value, valueStart); + T ret = visitItemBody(ctx); + delegate().endValue(sourceLocation(ctx.stop)); + end.end(sourceLocation(ctx.stop)); + return ret; + } + return defaultResult(); + } + + + @Override + public T visitMetadata(MetadataContext ctx) { + AxiomName identifier = statementIdentifier(ctx.itemBody().identifier()); + return processItemBody(identifier, ctx.itemBody(), delegate()::startInfra, delegate()::endInfra); + } + + private Object convert(ArgumentContext ctx) { + if (ctx.identifier() != null) { + return (convert(ctx.identifier())); + } else { + return (convert(ctx.string())); + } + } + + private boolean canEmit(AxiomName identifier) { + if (limit.isPresent()) { + return limit.get().contains(identifier); + } + return true; + } + + @Override + public final T visitArgument(ArgumentContext ctx) { + + return defaultResult(); + } + + private AxiomName convert(IdentifierContext argument) { + return argumentIdentifier(argument); + } + + private AxiomName argumentIdentifier(IdentifierContext identifier) { + String prefix = nullableText(identifier.prefix()); + String localName = identifier.localIdentifier().getText(); + return resolveArgument(prefix, localName); + } + + + private SourceLocation sourceLocation(Token start) { + return SourceLocation.from(sourceName, start.getLine(), start.getCharPositionInLine()); + } + + static String convert(StringContext string) { + if(string.singleQuoteString() != null) { + return convertSingleQuote(string.singleQuoteString().getText()); + } + if(string.doubleQuoteString() != null) { + return covertDoubleQuote(string.doubleQuoteString().getText()); + } + return convertMultiline(string.multilineString().getText()); + } + + private static String convertSingleQuote(String text) { + int stop = text.length(); + return text.substring(1, stop - 1); + } + + private static String covertDoubleQuote(String text) { + int stop = text.length(); + return text.substring(1, stop - 1); + } + + private static String convertMultiline(String text) { + return text.replace("\"\"\"", ""); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrStatementSource.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrStatementSource.java new file mode 100644 index 00000000000..8109f24cc51 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrStatementSource.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.antlr; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Set; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.stream.AxiomItemStream; +import com.evolveum.axiom.api.stream.AxiomItemStream.TargetWithResolver; +import com.evolveum.axiom.lang.antlr.AxiomParser.ItemContext; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + +public class AxiomAntlrStatementSource { + + private final ItemContext root; + private final String sourceName; + + public static AxiomAntlrStatementSource from(String sourceName, InputStream stream) throws IOException, AxiomSyntaxException { + return from(sourceName, CharStreams.fromStream(stream)); + } + + public static ItemContext contextFrom(String sourceName, CharStream stream) { + AxiomLexer lexer = new AxiomLexer(stream); + AxiomParser parser = new AxiomParser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + parser.removeErrorListeners(); + AxiomErrorListener errorListener = new AxiomErrorListener(sourceName); + parser.addErrorListener(errorListener); + ItemContext statement = parser.item(); + errorListener.validate(); + return statement; + } + + public static AxiomAntlrStatementSource from(String sourceName, CharStream stream) throws AxiomSyntaxException { + ItemContext statement = contextFrom(sourceName, stream); + return new AxiomAntlrStatementSource(sourceName, statement); + } + + protected AxiomAntlrStatementSource(String sourceName, ItemContext statement) { + this.sourceName = sourceName; + this.root = statement; + } + + public String sourceName() { + return sourceName; + } + + protected final ItemContext root() { + return root; + } + + public final void stream(AxiomItemStream.TargetWithResolver target) { + stream(target, Optional.empty()); + } + + public void stream(AxiomItemStream.TargetWithResolver target, Optional> emitOnly) { + stream(target, emitOnly, AxiomNameResolver.nullResolver()); + } + + public final void stream(TargetWithResolver target, Optional> emitOnly, + AxiomNameResolver resolver) { + AxiomAntlrVisitor2 visitor = new AxiomAntlrVisitor2<>(sourceName, target, emitOnly.orElse(null), resolver); + visitor.visit(root); + } + + public final void stream(AxiomNameResolver statements, AxiomNameResolver arguments, AxiomItemStream.Target listener, + Optional> emitOnly) { + AxiomAntlrVisitor visitor = new AxiomAntlrVisitor<>(sourceName, statements, arguments, listener, emitOnly.orElse(null)); + visitor.visit(root); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrVisitor.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrVisitor.java new file mode 100644 index 00000000000..28222843d2f --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrVisitor.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.antlr; + +import java.util.Set; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.stream.AxiomItemStream; +import com.evolveum.axiom.api.stream.AxiomItemStream.Target; +import com.evolveum.axiom.lang.antlr.AxiomParser.ArgumentContext; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; + +@Deprecated +public class AxiomAntlrVisitor extends AbstractAxiomAntlrVisitor { + + private final AxiomNameResolver statements; + private final AxiomNameResolver arguments; + private final AxiomItemStream.Target delegate; + + public AxiomAntlrVisitor(String name, AxiomNameResolver statements, AxiomNameResolver arguments, AxiomItemStream.Target delegate, + Set limit) { + super(name, limit); + this.statements = statements; + this.arguments = arguments; + this.delegate = delegate; + } + + @Override + protected AxiomName resolveArgument(String prefix, String localName) { + return arguments.resolveIdentifier(prefix, localName); + } + + @Override + protected AxiomName resolveItemName(String prefix, String localName) { + return statements.resolveIdentifier(prefix, localName); + } + + @Override + protected Target delegate() { + return delegate; + } + + public static String convertToString(ArgumentContext argument) { + if((argument.string() != null)) { + return convert(argument.string()); + } + return argument.getText(); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrVisitor2.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrVisitor2.java new file mode 100644 index 00000000000..5d47b45e9f1 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomAntlrVisitor2.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.antlr; + +import java.util.Set; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.stream.AxiomItemStream; +import com.evolveum.axiom.api.stream.AxiomItemStream.Target; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; + +public class AxiomAntlrVisitor2 extends AbstractAxiomAntlrVisitor { + + private final AxiomItemStream.TargetWithResolver delegate; + private final AxiomNameResolver sourceLocal; + + public AxiomAntlrVisitor2(String name, AxiomItemStream.TargetWithResolver delegate, + Set limit, AxiomNameResolver resolver) { + super(name, limit); + this.delegate = delegate; + this.sourceLocal = resolver; + } + + @Override + protected AxiomName resolveArgument(String prefix, String localName) { + return delegate.valueResolver().or(sourceLocal).resolveIdentifier(prefix, localName); + } + + @Override + protected AxiomName resolveItemName(String prefix, String localName) { + return delegate.itemResolver().or(sourceLocal).resolveIdentifier(prefix, localName); + } + + @Override + protected Target delegate() { + return delegate; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomErrorListener.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomErrorListener.java similarity index 77% rename from infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomErrorListener.java rename to infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomErrorListener.java index c8357f5d710..450e09de14f 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomErrorListener.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomErrorListener.java @@ -4,19 +4,17 @@ * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.axiom.lang.impl; +package com.evolveum.axiom.lang.antlr; import java.util.ArrayList; -import java.util.BitSet; import java.util.List; -import org.antlr.v4.runtime.ANTLRErrorListener; import org.antlr.v4.runtime.BaseErrorListener; -import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; -import org.antlr.v4.runtime.atn.ATNConfigSet; -import org.antlr.v4.runtime.dfa.DFA; + +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; public class AxiomErrorListener extends BaseErrorListener { @@ -30,8 +28,7 @@ public AxiomErrorListener(String source) { @Override public void syntaxError(final Recognizer recognizer, final Object offendingSymbol, final int line, final int charPositionInLine, final String msg, final RecognitionException e) { - exceptions.add(new AxiomSyntaxException(source, line, charPositionInLine, msg)); - + exceptions.add(new AxiomSyntaxException(SourceLocation.from(source, line, charPositionInLine), msg)); } public void validate() throws AxiomSyntaxException { @@ -49,6 +46,6 @@ public void validate() throws AxiomSyntaxException { sb.append(e.getFormattedMessage()); } - throw new AxiomSyntaxException(source, 0, 0, sb.toString()); + throw new AxiomSyntaxException(SourceLocation.from(source, 0, 0), sb.toString()); } } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomModelStatementSource.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomModelStatementSource.java new file mode 100644 index 00000000000..d26178d27bb --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/antlr/AxiomModelStatementSource.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.antlr; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.stream.AxiomItemStream.TargetWithResolver; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.antlr.AxiomParser.ItemContext; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + +public class AxiomModelStatementSource extends AxiomAntlrStatementSource implements AxiomNameResolver { + + private static final String IMPORT = "import"; + private static final String NAMESPACE = "namespace"; + private static final String PREFIX = "prefix"; + + private String name; + private Map imports; + private String namespace; + + public static AxiomModelStatementSource from(InputStream stream) throws IOException, AxiomSyntaxException { + return from(null, CharStreams.fromStream(stream)); + } + + public static AxiomModelStatementSource from(String sourceName, InputStream stream) throws IOException, AxiomSyntaxException { + return from(sourceName, CharStreams.fromStream(stream)); + } + + public static AxiomModelStatementSource from(String sourceName, CharStream stream) throws AxiomSyntaxException { + try { + ItemContext root = AxiomAntlrStatementSource.contextFrom(sourceName, stream); + String name = root.itemBody().value().argument().identifier().localIdentifier().getText(); + return new AxiomModelStatementSource(sourceName, root, name, namespace(root.itemBody().value()), imports(root.itemBody().value())); + } catch (AxiomSyntaxException e) { + throw e; + } catch (Exception e) { + throw new AxiomSyntaxException(SourceLocation.from(sourceName, 0, 0), "Unexpected error", e); + } + } + + private AxiomModelStatementSource(String sourceName, ItemContext statement, String namespace, String name, Map imports) { + super(sourceName, statement); + this.name = name; + this.imports = imports; + this.namespace = namespace; + } + + + public String modelName() { + return name; + } + + public String namespace() { + return namespace; + } + + @Override + public void stream(TargetWithResolver target, Optional> emitOnly) { + stream(target, emitOnly, this); + } + + public Map imports() { + return imports; + } + + // FIXME: Use schema & AxiomItemTarget to get base model data? + public static Map imports(AxiomParser.ValueContext root) { + Map prefixMap = new HashMap<>(); + root.item().stream().filter(s -> IMPORT.equals(s.itemBody().identifier().getText())).forEach(c -> { + String namespace = AxiomAntlrVisitor.convert(c.itemBody().value().argument().string()); + String prefix = prefix(c.itemBody().value()); + prefixMap.put(prefix, namespace); + }); + prefixMap.put("",namespace(root)); + return prefixMap; + } + + + private static String namespace(AxiomParser.ValueContext c) { + return AxiomAntlrVisitor.convert(c.item() + .stream().filter(s -> NAMESPACE.equals(s.itemBody().identifier().getText())) + .findFirst().get().itemBody().value().argument().string()); + } + + private static String prefix(AxiomParser.ValueContext c) { + return AxiomAntlrVisitor.convertToString(c.item() + .stream().filter(s -> PREFIX.equals(s.itemBody().identifier().getText())) + .findFirst().get().itemBody().value().argument()); + } + + @Override + public AxiomName resolveIdentifier(@Nullable String prefix, @NotNull String localName) { + if(prefix == null) { + prefix = ""; + } + String maybeNs = imports.get(prefix); + if(maybeNs != null) { + return AxiomName.from(maybeNs, localName); + } + return null; + } +} 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 f76845e020a..7b3dd106e14 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 @@ -11,17 +11,18 @@ import java.util.Map; import java.util.Optional; -import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; import com.evolveum.axiom.concepts.Lazy; -import com.evolveum.axiom.concepts.Lazy.Supplier; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.collect.ImmutableSet; public class AxiomBuiltIn { - public static final Lazy> EMPTY = Lazy.instant(ImmutableMap.of()); + public static final Lazy> EMPTY = Lazy.instant(ImmutableMap.of()); public static final Lazy NO_ARGUMENT = Lazy.nullValue(); @@ -29,7 +30,9 @@ private AxiomBuiltIn() { throw new UnsupportedOperationException("Utility class"); } - public static class Item implements AxiomItemDefinition, AxiomStatement { + + + public static class Item implements AxiomItemDefinition { public static final Item NAME = new Item("name", Type.IDENTIFIER, true); public static final Item ARGUMENT = new Item("argument", Type.IDENTIFIER, false); public static final AxiomItemDefinition DOCUMENTATION = new Item("documentation", Type.STRING, true); @@ -37,13 +40,21 @@ public static class Item implements AxiomItemDefinition, AxiomStatement identifierDefinition() { + return Optional.of(NAME_IDENTIFIER.get()); + } + }; public static final Item MODEL_DEFINITION = new Item("model", Type.MODEL, false); public static final AxiomItemDefinition MIN_OCCURS = new Item("minOccurs", Type.STRING, false); public static final AxiomItemDefinition MAX_OCCURS = new Item("maxOccurs", Type.STRING, false); public static final AxiomItemDefinition TARGET_TYPE = new Item("targetType", Type.IDENTIFIER, true); + public static final AxiomItemDefinition OPERATIONAL = new Item("operational", Type.IDENTIFIER, true); public static final AxiomItemDefinition IDENTIFIER_DEFINITION = new Item("identifier", Type.IDENTIFIER_DEFINITION, true); @@ -51,20 +62,33 @@ public static class Item implements AxiomItemDefinition, AxiomStatement NAME_IDENTIFIER = Lazy.from( + ()-> (AxiomIdentifierDefinition.parent(ITEM_DEFINITION.name(), Item.NAME.name()))); - private final AxiomIdentifier identifier; + private final AxiomName identifier; private final AxiomTypeDefinition type; private boolean required; private Item(String identifier, AxiomTypeDefinition type, boolean required) { - this.identifier = AxiomIdentifier.axiom(identifier); + this.identifier = AxiomName.axiom(identifier); this.type = type; this.required = required; } @Override - public AxiomIdentifier name() { + public Optional type() { + return Optional.of(type); + } + + @Override + public AxiomName name() { return identifier; } @@ -75,7 +99,7 @@ public String documentation() { @Override - public AxiomTypeDefinition type() { + public AxiomTypeDefinition typeDefinition() { return type; } @@ -84,36 +108,56 @@ public boolean required() { return required; } + @Override + public int minOccurs() { + return 0; + } + + @Override + public boolean operational() { + return false; + } + + @Override + public int maxOccurs() { + return Integer.MAX_VALUE; + } + @Override public String toString() { return AxiomItemDefinition.toString(this); } @Override - public Collection> children() { + public AxiomTypeDefinition definingType() { return null; } @Override - public Collection> children(AxiomIdentifier name) { - return null; + public Optional identifierDefinition() { + return Optional.empty(); } @Override - public AxiomIdentifier keyword() { + public Map> itemMap() { return null; } + @Override - public AxiomIdentifier value() { - return identifier; + public Map> infraItems() { + return null; } } public static class Type implements AxiomTypeDefinition { public static final Type UUID = new Type("uuid"); public static final Type STRING = new Type("string"); - public static final Type IDENTIFIER = new Type("AxiomIdentifier"); - public static final Type TYPE_REFERENCE = new Type("AxiomTypeReference"); + public static final Type IDENTIFIER = new Type("AxiomName"); + + public static final Type TYPE_REFERENCE = new Type("AxiomTypeReference", null, () -> Item.NAME, () -> itemDefs( + Item.NAME, + Item.REF_TARGET + )); public static final Type BASE_DEFINITION = new Type("AxiomBaseDefinition", null, () -> Item.NAME, () -> itemDefs( Item.NAME, @@ -132,48 +176,21 @@ public static class Type implements AxiomTypeDefinition { public static final Type TYPE_DEFINITION = new Type("AxiomTypeDefinition", BASE_DEFINITION, () -> itemDefs( Item.ARGUMENT, - Item.IDENTIFIER_DEFINITION, Item.SUPERTYPE_REFERENCE, Item.ITEM_DEFINITION - )) { - @Override - public Collection identifiers() { - return TYPE_IDENTIFIER_DEFINITION.get(); - } - }; - - protected static final Lazy> TYPE_IDENTIFIER_DEFINITION = Lazy.from(()-> - ImmutableSet.of(AxiomIdentifierDefinition.global(TYPE_DEFINITION.name(), Item.NAME))); + )); public static final Type ITEM_DEFINITION = new Type("AxiomItemDefinition", BASE_DEFINITION, () -> itemDefs( Item.TYPE_REFERENCE, + Item.IDENTIFIER_DEFINITION, Item.MIN_OCCURS, - Item.MAX_OCCURS - )) { - - @Override - public Collection identifiers() { - return ITEM_IDENTIFIER_DEFINITION.get(); - } - }; - - public static final Type ROOT_DEFINITION = - new Type("AxiomRootDefinition", ITEM_DEFINITION, () -> itemDefs()) { - - @Override - public Collection identifiers() { - return ROOT_IDENTIFIER_DEFINITION.get(); - } - }; - - protected static final Lazy> ITEM_IDENTIFIER_DEFINITION = Lazy.from(()-> - ImmutableSet.of(AxiomIdentifierDefinition.local(ITEM_DEFINITION.name(), Item.NAME))); - - protected static final Lazy> ROOT_IDENTIFIER_DEFINITION = Lazy.from(()-> - ImmutableSet.of(AxiomIdentifierDefinition.global(AxiomItemDefinition.ROOT_SPACE, Item.NAME))); + Item.MAX_OCCURS, + Item.OPERATIONAL + )); + public static final Type ROOT_DEFINITION = new Type("AxiomRootDefinition", ITEM_DEFINITION); public static final Type IDENTIFIER_DEFINITION = new Type("AxiomIdentifierDefinition", BASE_DEFINITION, () -> Item.ID_MEMBER, () -> itemDefs( @@ -181,40 +198,52 @@ public Collection identifiers() { Item.ID_SCOPE, Item.ID_SPACE )); - private final AxiomIdentifier identifier; + public static final Type IMPORT_DEFINITION = new Type("AxiomImportDeclaration"); + public static final Type AUGMENTATION_DEFINITION = new Type("AxiomAugmentationDefinition",TYPE_DEFINITION); + + public static final Type AXIOM_VALUE = new Type("AxiomValue", null, () -> itemDefs( + Item.TYPE_REFERENCE, + Item.VALUE + )); + + private final AxiomName identifier; private final AxiomTypeDefinition superType; private final Lazy argument; - private final Lazy> items; + private final Lazy> items; private Type(String identifier) { this(identifier, null, Lazy.nullValue(), EMPTY); } - private Type(String identifier, Lazy.Supplier> items) { + private Type(String identifier, Lazy.Supplier> items) { this(identifier, null, Lazy.nullValue(), Lazy.from(items)); } - private Type(String identifier, AxiomTypeDefinition superType, Lazy.Supplier> items) { + private Type(String identifier, AxiomTypeDefinition superType) { + this(identifier, superType, NO_ARGUMENT, EMPTY); + } + + private Type(String identifier, AxiomTypeDefinition superType, Lazy.Supplier> items) { this(identifier, superType, NO_ARGUMENT, Lazy.from(items)); } private Type(String identifier, AxiomTypeDefinition superType, Lazy.Supplier argument, - Lazy.Supplier> items) { + Lazy.Supplier> items) { this(identifier, superType, Lazy.from(argument), Lazy.from(items)); } private Type(String identifier, AxiomTypeDefinition superType, Lazy argument, - Lazy> items) { - this.identifier = AxiomIdentifier.axiom(identifier); + Lazy> items) { + this.identifier = AxiomName.axiom(identifier); this.argument = argument; this.superType = superType; this.items = items; } @Override - public AxiomIdentifier name() { + public AxiomName name() { return identifier; } @@ -229,12 +258,12 @@ public Optional superType() { } @Override - public Map items() { + public Map itemDefinitions() { return items.get(); } - private static Map itemDefs(AxiomItemDefinition... items) { - Builder builder = ImmutableMap.builder(); + private static Map itemDefs(AxiomItemDefinition... items) { + Builder builder = ImmutableMap.builder(); for (AxiomItemDefinition item : items) { builder.put(item.name(), item); @@ -243,7 +272,7 @@ private static Map itemDefs(AxiomItemDefin } @Override - public Collection identifiers() { + public Collection identifierDefinitions() { return Collections.emptyList(); } @@ -258,6 +287,29 @@ public Optional argument() { return Optional.empty(); } + @Override + public String toString() { + // TODO Auto-generated method stub + return "typedef " + name(); + } + + @Override + public Map> itemMap() { + return null; + } + + @Override + public boolean isComplex() { + if(superType != null && superType.isComplex()) { + return true; + } + return !itemDefinitions().isEmpty(); + } + + @Override + public Map> infraItems() { + return null; + } } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomIdentifierDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomIdentifierDefinition.java deleted file mode 100644 index d1985875020..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomIdentifierDefinition.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.evolveum.axiom.lang.api; - -import java.util.Collection; -import java.util.Optional; -import java.util.Set; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.google.common.collect.ImmutableSet; - -public interface AxiomIdentifierDefinition { - - Collection components(); - - Scope scope(); - - AxiomIdentifier space(); - - enum Scope { - GLOBAL, - LOCAL - } - - static AxiomIdentifierDefinition global(AxiomIdentifier name, AxiomItemDefinition... components) { - return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(components), name, Scope.GLOBAL); - } - - static AxiomIdentifierDefinition local(AxiomIdentifier name, AxiomItemDefinition... components) { - return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(components), name, Scope.LOCAL); - } - - static Scope scope(String scope) { - if(Scope.GLOBAL.name().equalsIgnoreCase(scope)) { - return Scope.GLOBAL; - } - if(Scope.LOCAL.name().equalsIgnoreCase(scope)) { - return Scope.LOCAL; - } - throw new IllegalArgumentException("Unknown scope " + scope); - } - - static AxiomIdentifierDefinition from(AxiomIdentifier space, Scope scope, Set members) { - return new AxiomIdentifierDefinitionImpl(ImmutableSet.copyOf(members), space, scope); - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomIdentifierDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomIdentifierDefinitionImpl.java deleted file mode 100644 index de7b6c75340..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomIdentifierDefinitionImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.evolveum.axiom.lang.api; - -import java.util.Collection; -import java.util.Set; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.google.common.collect.ImmutableSet; - -class AxiomIdentifierDefinitionImpl implements AxiomIdentifierDefinition { - - private Set components; - - private AxiomIdentifier space; - - private Scope scope; - - public AxiomIdentifierDefinitionImpl(Set components, AxiomIdentifier space, Scope scope) { - super(); - this.components = ImmutableSet.copyOf(components); - this.space = space; - this.scope = scope; - } - - @Override - public Set components() { - return components; - } - - @Override - public Scope scope() { - return scope; - } - - @Override - public AxiomIdentifier space() { - return space; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomItemDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomItemDefinition.java deleted file mode 100644 index d37cc6cee47..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomItemDefinition.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.api; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.google.common.base.MoreObjects; - -public interface AxiomItemDefinition extends AxiomBaseDefinition { - - AxiomIdentifier ROOT_SPACE = AxiomIdentifier.axiom("AxiomRootDefinition"); - - AxiomTypeDefinition type(); - boolean required(); - - static String toString(AxiomItemDefinition def) { - return MoreObjects.toStringHelper(AxiomItemDefinition.class) - .add("name", def.name()) - .add("type", def.type()) - .toString(); - } -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomModel.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomModel.java index be093c92b43..398feda6121 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomModel.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomModel.java @@ -1,5 +1,13 @@ package com.evolveum.axiom.lang.api; +import com.evolveum.axiom.api.AxiomName; + public interface AxiomModel { + AxiomName NAMESPACE = AxiomName.axiom("namespace"); + AxiomName IMPORTED_NAMESPACE = AxiomName.axiom("ImportedNamespace"); + String BUILTIN_TYPES = "https://schema.evolveum.com/ns/axiom/model"; + + String name(); + String namespace(); } 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 deleted file mode 100644 index 89e8565aa38..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomSchemaContext.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.evolveum.axiom.lang.api; - -import java.util.Collection; -import java.util.Optional; - -import com.evolveum.axiom.api.AxiomIdentifier; - -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/api/AxiomTypeDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomTypeDefinition.java deleted file mode 100644 index bbaea4573c5..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/AxiomTypeDefinition.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.api; - -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.google.common.collect.ImmutableMap; - -public interface AxiomTypeDefinition extends AxiomBaseDefinition { - - public final AxiomIdentifier IDENTIFIER_MEMBER = AxiomIdentifier.axiom("name"); - public final AxiomIdentifier IDENTIFIER_SPACE = AxiomIdentifier.axiom("AxiomTypeDefinition"); - - Optional argument(); - - Optional superType(); - - Map items(); - - Collection identifiers(); - - default Optional item(AxiomIdentifier child) { - AxiomItemDefinition maybe = items().get(child); - if(maybe != null) { - return Optional.of(maybe); - } - if(superType().isPresent()) { - return superType().get().item(child); - } - return Optional.empty(); - } - - static IdentifierSpaceKey identifier(AxiomIdentifier name) { - return IdentifierSpaceKey.from(ImmutableMap.of(IDENTIFIER_MEMBER, name)); - } - - default Collection requiredItems() { - return items().values().stream().filter(AxiomItemDefinition::required).collect(Collectors.toList()); - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/IdentifierSpaceKey.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/IdentifierSpaceKey.java index 7fe6d301e42..ca8318ec2e3 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/IdentifierSpaceKey.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/IdentifierSpaceKey.java @@ -3,18 +3,18 @@ import java.util.Map; import java.util.Map.Entry; -import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.api.AxiomName; import com.google.common.collect.ImmutableMap; public class IdentifierSpaceKey { - private final Map components; + private final Map components; - public IdentifierSpaceKey(Map components) { + public IdentifierSpaceKey(Map components) { this.components = ImmutableMap.copyOf(components); } - public Map components() { + public Map components() { return components; } @@ -34,7 +34,7 @@ public boolean equals(Object obj) { return false; } - public static IdentifierSpaceKey from(Map build) { + public static IdentifierSpaceKey from(Map build) { return new IdentifierSpaceKey(build); } @@ -43,7 +43,7 @@ public String toString() { StringBuilder b = new StringBuilder(); b.append("["); boolean first = true; - for(Entry val : components().entrySet()) { + for(Entry val : components().entrySet()) { if(!first) { b.append(","); } @@ -53,7 +53,8 @@ public String toString() { return b.toString(); } - public static IdentifierSpaceKey of(AxiomIdentifier key, Object value) { + public static IdentifierSpaceKey of(AxiomName key, Object value) { return from(ImmutableMap.of(key, value)); } + } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/AxiomStatement.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/AxiomStatement.java deleted file mode 100644 index d320f89c71f..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/AxiomStatement.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.api.stmt; - -import java.util.Collection; -import java.util.Optional; -import java.util.stream.Collectors; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.google.common.collect.Collections2; -import com.google.common.collect.Iterables; - -public interface AxiomStatement { - - AxiomIdentifier keyword(); - V value(); - - Collection> children(); - - Collection> children(AxiomIdentifier name); - - default Collection children(AxiomIdentifier name, Class type) { - return children(name).stream().filter(type::isInstance).map(type::cast).collect(Collectors.toList()); - } - - default Optional first(AxiomIdentifier axiomIdentifier, Class class1) { - return children(axiomIdentifier).stream().filter(class1::isInstance).findFirst().map(class1::cast); - } - - default Optional> first(AxiomItemDefinition item) { - return first(item.name()); - } - - default Optional> first(AxiomIdentifier name) { - return children(name).stream().findFirst(); - } - - default Optional firstValue(AxiomIdentifier name, Class type) { - return children(name).stream().filter(s -> type.isInstance(s.value())).map(s -> type.cast(s.value())).findFirst(); - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/AxiomStatementStreamListener.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/AxiomStatementStreamListener.java deleted file mode 100644 index ad2ff098907..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/api/stmt/AxiomStatementStreamListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.api.stmt; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.impl.AxiomSyntaxException; - -public interface AxiomStatementStreamListener { - - void endStatement( @Nullable SourceLocation sourceLocation); - void startStatement(@NotNull AxiomIdentifier identifier, @Nullable SourceLocation sourceLocation) throws AxiomSyntaxException; - void argument(@NotNull AxiomIdentifier convert, @Nullable SourceLocation sourceLocation); - void argument(@NotNull String convert, @Nullable SourceLocation sourceLocation); -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AbstractAxiomBaseDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AbstractAxiomBaseDefinition.java deleted file mode 100644 index 9298b9a69e1..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AbstractAxiomBaseDefinition.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.List; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomBaseDefinition; -import com.evolveum.axiom.lang.api.AxiomBuiltIn; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; -import com.google.common.collect.Multimap; - -public class AbstractAxiomBaseDefinition extends AxiomStatementImpl implements AxiomBaseDefinition { - - public static final Factory FACTORY = AbstractAxiomBaseDefinition::new ; - private AxiomIdentifier name; - private String documentation; - - public AbstractAxiomBaseDefinition(AxiomIdentifier keyword, AxiomIdentifier value, List> children, - Multimap> keywordMap) { - super(keyword, value, children, keywordMap); - name = firstValue(AxiomBuiltIn.Item.NAME.name(), AxiomIdentifier.class).get(); - } - - @Override - public AxiomIdentifier name() { - return name; - } - - @Override - public String documentation() { - return documentation; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AbstractContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AbstractContext.java new file mode 100644 index 00000000000..f3a31ff9195 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AbstractContext.java @@ -0,0 +1,73 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; + +abstract class AbstractContext

> implements IdentifierSpaceHolder { + + private final P parent; + private SourceLocation start; + + private final IdentifierSpaceHolder localSpace; + + public AbstractContext(P context, SourceLocation loc, Scope scope) { + this(context,loc, new IdentifierSpaceHolderImpl(scope)); + } + + public AbstractContext(P context, SourceLocation loc, IdentifierSpaceHolder space) { + parent = context; + start = loc; + localSpace = space; + } + + public P parent() { + return parent; + } + protected abstract Optional childItemDef(AxiomName id); + + + protected SourceContext rootImpl() { + return parent.rootImpl(); + } + + public SourceLocation startLocation() { + return start; + } + + @Override + public ValueContext lookup(AxiomName space, IdentifierSpaceKey key) { + ValueContext maybe = localSpace.lookup(space, key); + if(maybe != null) { + return maybe; + } + return parent().lookup(space, key); + } + + @Override + public void register(AxiomName space, Scope scope, IdentifierSpaceKey key, ValueContext context) { + switch (scope) { + case GLOBAL: + rootImpl().register(space, scope, key, context); + break; + case PARENT: + parent().register(space, Scope.LOCAL, key, context); + break; + case LOCAL: + localSpace.register(space, scope, key, context); + break; + default: + throw new IllegalStateException("Unsupported scope"); + } + } + + @Override + public Map> space(AxiomName space) { + return localSpace.space(space); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomAntlrVisitor.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomAntlrVisitor.java deleted file mode 100644 index fa6dc8b4e99..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomAntlrVisitor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import java.util.Optional; -import java.util.Set; - -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.RuleNode; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.antlr.AxiomBaseVisitor; -import com.evolveum.axiom.lang.antlr.AxiomParser.ArgumentContext; -import com.evolveum.axiom.lang.antlr.AxiomParser.IdentifierContext; -import com.evolveum.axiom.lang.antlr.AxiomParser.PrefixContext; -import com.evolveum.axiom.lang.antlr.AxiomParser.StatementContext; -import com.evolveum.axiom.lang.antlr.AxiomParser.StringContext; -import com.evolveum.axiom.lang.api.stmt.AxiomStatementStreamListener; -import com.evolveum.axiom.lang.api.stmt.SourceLocation; -import com.google.common.base.Strings; - -public class AxiomAntlrVisitor extends AxiomBaseVisitor { - - private final AxiomIdentifierResolver statements; - private final AxiomStatementStreamListener delegate; - private final Optional> limit; - private final String sourceName; - - public AxiomAntlrVisitor(String name, AxiomIdentifierResolver statements, AxiomStatementStreamListener delegate, - Set limit) { - this.sourceName = name; - this.statements = statements; - this.delegate = delegate; - this.limit = Optional.ofNullable(limit); - } - - private AxiomIdentifier statementIdentifier(IdentifierContext identifier) { - String prefix = nullableText(identifier.prefix()); - String localName = identifier.localIdentifier().getText(); - return statements.resolveStatementIdentifier(prefix, localName); - } - - private String nullableText(ParserRuleContext prefix) { - return prefix != null ? prefix.getText() : null; - } - - @Override - public T visitStatement(StatementContext ctx) { - AxiomIdentifier identifier = statementIdentifier(ctx.identifier()); - if(canEmit(identifier)) { - delegate.startStatement(identifier, sourceLocation(ctx.identifier().start)); - T ret = super.visitStatement(ctx); - delegate.endStatement(sourceLocation(ctx.stop)); - return ret; - } - return defaultResult(); - } - - private boolean canEmit(AxiomIdentifier identifier) { - if (limit.isPresent()) { - return limit.get().contains(identifier); - } - return true; - } - - @Override - public T visitArgument(ArgumentContext ctx) { - if (ctx.identifier() != null) { - delegate.argument(convert(ctx.identifier()), sourceLocation(ctx.start)); - } else { - delegate.argument(convert(ctx.string()), sourceLocation(ctx.start)); - } - return defaultResult(); - } - - private AxiomIdentifier convert(IdentifierContext argument) { - return statementIdentifier(argument); - } - - private String convert(StringContext string) { - if(string.singleQuoteString() != null) { - return convertSingleQuote(string.singleQuoteString().getText()); - } - if(string.doubleQuoteString() != null) { - return covertDoubleQuote(string.doubleQuoteString().getText()); - } - return convertMultiline(string.multilineString().getText()); - } - - private int sourceLine(ParserRuleContext node) { - return node.start.getLine(); - } - - private SourceLocation sourceLocation(Token start) { - return SourceLocation.from(sourceName, start.getLine(), start.getCharPositionInLine()); - } - - private String convertSingleQuote(String text) { - int stop = text.length(); - return text.substring(1, stop - 1); - } - - private String covertDoubleQuote(String text) { - int stop = text.length(); - return text.substring(1, stop - 1); - } - - private String convertMultiline(String text) { - return text.replace("\"\"\"", ""); - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomIdentifierResolver.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomIdentifierResolver.java deleted file mode 100644 index 89650a2cdeb..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomIdentifierResolver.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.axiom.api.AxiomIdentifier; - -import org.jetbrains.annotations.Nullable; - -public interface AxiomIdentifierResolver { - - final AxiomIdentifierResolver AXIOM_DEFAULT_NAMESPACE = defaultNamespace(AxiomIdentifier.AXIOM_NAMESPACE); - - AxiomIdentifier resolveStatementIdentifier(@Nullable String prefix, @NotNull String localName); - - static AxiomIdentifierResolver defaultNamespace(String namespace) { - return (prefix, localName) -> prefix == null ? AxiomIdentifier.from(namespace, localName) : null; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomItemContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomItemContext.java new file mode 100644 index 00000000000..48eeed9c865 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomItemContext.java @@ -0,0 +1,13 @@ +package com.evolveum.axiom.lang.impl; + +public interface AxiomItemContext { + + AxiomValueContext addValue(T value); + + AxiomValueContext parent(); + + T onlyValue(); + + void addOperationalValue(AxiomValueReference value); + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomItemDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomItemDefinitionImpl.java deleted file mode 100644 index 6e80dd0f8d8..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomItemDefinitionImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.List; - -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.AxiomTypeDefinition; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; -import com.google.common.collect.Multimap; - -public class AxiomItemDefinitionImpl extends AbstractAxiomBaseDefinition implements AxiomItemDefinition { - - public static final Factory FACTORY = AxiomItemDefinitionImpl::new ; - private final AxiomTypeDefinition type; - private int minOccurs; - - public AxiomItemDefinitionImpl(AxiomIdentifier keyword, AxiomIdentifier value, List> children, - Multimap> keywordMap) { - super(keyword, value, children, keywordMap); - type = first(AxiomBuiltIn.Item.TYPE_DEFINITION.name(), AxiomTypeDefinition.class) - .orElseThrow(() -> new IllegalStateException("No 'type' declaration in " + super.toString())); - minOccurs = firstValue(AxiomBuiltIn.Item.MIN_OCCURS.name(), String.class).map(Integer::parseInt).orElse(0); - } - - @Override - public AxiomTypeDefinition type() { - return type; - } - - @Override - public boolean required() { - return minOccurs > 0; - } - - @Override - public String toString() { - return AxiomItemDefinition.toString(this); - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomModelInfo.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomModelInfo.java deleted file mode 100644 index 80f13954a47..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomModelInfo.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -public interface AxiomModelInfo { - - String getModelName(); - String getNamespace(); - String getDescription(); -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomRootContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomRootContext.java new file mode 100644 index 00000000000..0130a69e195 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomRootContext.java @@ -0,0 +1,11 @@ +package com.evolveum.axiom.lang.impl; + +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; + +public interface AxiomRootContext { + + void importIdentifierSpace(NamespaceContext namespaceContext); + + void exportIdentifierSpace(IdentifierSpaceKey namespaceId); + +} 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 index 10da0252199..d8a24de4c90 100644 --- 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 @@ -4,13 +4,13 @@ import java.util.Map; import java.util.Optional; -import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; 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.IdentifierSpaceKey; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; @@ -18,12 +18,12 @@ public class AxiomSchemaContextImpl implements AxiomSchemaContext { private Map roots; private Map types; - private Map>> globals; + private Map>> globals; - public AxiomSchemaContextImpl(Map>> globalMap) { + public AxiomSchemaContextImpl(Map>> globalMap) { this.globals = globalMap; this.roots = Maps.transformValues(globalMap.get(AxiomItemDefinition.ROOT_SPACE), AxiomItemDefinition.class::cast); - this.types = Maps.transformValues(globalMap.get(AxiomTypeDefinition.IDENTIFIER_SPACE), AxiomTypeDefinition.class::cast); + this.types = Maps.transformValues(globalMap.get(AxiomTypeDefinition.SPACE), AxiomTypeDefinition.class::cast); } @Override @@ -32,7 +32,7 @@ public Collection roots() { } @Override - public Optional getType(AxiomIdentifier type) { + public Optional getType(AxiomName type) { return Optional.ofNullable(types.get(nameKey(type))); } @@ -42,18 +42,18 @@ public Collection types() { } @Override - public Optional getRoot(AxiomIdentifier type) { + public Optional getRoot(AxiomName type) { return Optional.ofNullable(roots.get(nameKey(type))); } - private static IdentifierSpaceKey nameKey(AxiomIdentifier type) { + private static IdentifierSpaceKey nameKey(AxiomName type) { return IdentifierSpaceKey.of(AxiomTypeDefinition.IDENTIFIER_MEMBER, type); } public static AxiomSchemaContextImpl boostrapContext() { - Map> root = ImmutableMap.of(nameKey(AxiomBuiltIn.Item.MODEL_DEFINITION.name()), AxiomBuiltIn.Item.MODEL_DEFINITION); - Map>> global - = ImmutableMap.of(AxiomItemDefinition.ROOT_SPACE, root, AxiomTypeDefinition.IDENTIFIER_SPACE, ImmutableMap.of()); + Map> root = ImmutableMap.of(nameKey(AxiomBuiltIn.Item.MODEL_DEFINITION.name()), AxiomBuiltIn.Item.MODEL_DEFINITION); + Map>> global + = ImmutableMap.of(AxiomItemDefinition.ROOT_SPACE, root, AxiomTypeDefinition.SPACE, ImmutableMap.of()); return new AxiomSchemaContextImpl(global); } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSemanticException.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSemanticException.java deleted file mode 100644 index 9c345456e9d..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSemanticException.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.stmt.SourceLocation; -import com.google.common.base.Strings; - -public class AxiomSemanticException extends RuntimeException { - - - - public AxiomSemanticException(String message, Throwable cause) { - super(message, cause); - } - - public AxiomSemanticException(String message) { - super(message); - } - - public AxiomSemanticException(Throwable cause) { - super(cause); - } - - public AxiomSemanticException(AxiomItemDefinition definition, String message) { - super(definition.toString() + " " + message); - } - - public static V checkNotNull(V value, AxiomItemDefinition definition, String message) throws AxiomSemanticException { - if(value == null) { - throw new AxiomSemanticException(definition, message); - } - return value; - } - - public static void check(boolean check, SourceLocation start, String format, Object... arguments) { - if(!check) { - throw new AxiomSemanticException(start + Strings.lenientFormat(format, arguments)); - } - ; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementBuilder.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementBuilder.java deleted file mode 100644 index 4d3b2eafebb..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementBuilder.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import com.evolveum.axiom.concepts.Lazy; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -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.stmt.AxiomStatement; -import com.evolveum.axiom.lang.impl.AxiomStatementImpl.Factory; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; - -public class AxiomStatementBuilder implements Lazy.Supplier> { - - private final AxiomIdentifier identifier; - private final AxiomStatementImpl.Factory> factory; - private T value; - - public AxiomStatementBuilder(AxiomIdentifier identifier, Factory> factory) { - this.identifier = identifier; - this.factory = factory; - } - - public AxiomStatementBuilder(AxiomIdentifier identifier) { - this(identifier, AxiomStatementImpl.factory()); - } - - - public T getValue() { - return value; - } - - public void setValue(T value) { - this.value = value; - } - - private List>> childList = new ArrayList<>(); - private Multimap>> children = HashMultimap.create(); - - public void add(AxiomItemDefinition item, Supplier> statement) { - add(item.name(), statement); - } - - public void add(AxiomIdentifier type, Supplier> statement) { - childList.add(statement); - children.put(type, statement); - } - - @Override - public AxiomStatement get() { - return factory.create(identifier, value, buildChildList(), buildChildMap()); - } - - private Multimap> buildChildMap() { - return Multimaps.transformValues(children, (v) -> v.get()); - } - - private List> buildChildList() { - return Lists.transform(childList, (v) -> v.get()); - } -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementFactoryContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementFactoryContext.java deleted file mode 100644 index fc3fc6a8341..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementFactoryContext.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; -import com.evolveum.axiom.lang.impl.AxiomStatementImpl.Factory; - -public interface AxiomStatementFactoryContext { - - AxiomStatementImpl.Factory factoryFor(AxiomTypeDefinition identifier); - - static AxiomStatementFactoryContext defaultFactory(AxiomStatementImpl.Factory factory) { - return (identifier) -> factory; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementImpl.java deleted file mode 100644 index 9bcf42362ad..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementImpl.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -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.ArrayListMultimap; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.ImmutableMap.Builder; - -public class AxiomStatementImpl implements AxiomStatement { - - private final AxiomIdentifier keyword; - private final V value; - private final List> children; - private final Multimap> keywordMap; - - public AxiomStatementImpl(AxiomIdentifier keyword, V value, List> children, - Multimap> keywordMap) { - super(); - this.keyword = keyword; - this.value = value; - this.children = ImmutableList.copyOf(children); - this.keywordMap = ImmutableMultimap.copyOf(keywordMap); - } - - @Override - public Collection> children(AxiomIdentifier type) { - return keywordMap.get(type); - } - - @Override - public Collection> children() { - return children; - } - - @Override - public AxiomIdentifier keyword() { - return keyword; - } - - @Override - public V value() { - return value; - } - - @Override - public String toString() { - return keyword + "{value=" + value + "}"; - } - - interface Factory> { - - I create(AxiomIdentifier type, V value, List> children, - Multimap> keywordMap); - - } - - protected void putAll(Builder builder, - Collection children) { - for (AxiomItemDefinition definition : children) { - builder.put(definition.name(), definition); - } - } - - public static > Factory factory() { - return (Factory) AxiomStatementImpl::new; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementRule.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementRule.java new file mode 100644 index 00000000000..b3e5a6f0fd9 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementRule.java @@ -0,0 +1,101 @@ +package com.evolveum.axiom.lang.impl; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.evolveum.axiom.reactor.Dependency; + +public interface AxiomStatementRule { + + String name(); + + boolean isApplicableTo(AxiomItemDefinition definition); + + void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException; + + + interface Lookup { + default AxiomTypeDefinition typeDefinition() { + return itemDefinition().typeDefinition(); + } + + AxiomItemDefinition itemDefinition(); + + Dependency namespace(AxiomName name, IdentifierSpaceKey namespaceId); + + Dependency> child(AxiomItemDefinition item, Class valueType); + + Dependency> child(AxiomName item, Class valueType); + + Dependency> onlyItemValue(AxiomItemDefinition item, Class valueType); + + Dependency> modify(AxiomName identifierSpace, IdentifierSpaceKey identifier); + + Dependency.Search> global(AxiomName identifierSpace, IdentifierSpaceKey identifier); + + Dependency.Search> reference(AxiomName identifierSpace, IdentifierSpaceKey identifier); + + Dependency.Search> namespaceValue(AxiomName space, IdentifierSpaceKey itemName); + + Dependency finalValue(); + + V currentValue(); + + V originalValue(); + + boolean isMutable(); + + Lookup parentValue(); + + AxiomSemanticException error(String message, Object... arguments); + } + + interface ActionBuilder { + + + AxiomSemanticException error(String message, Object... arguments); + + ActionBuilder apply(Action action); + + Dependency> require(AxiomValueContext ext); + + + + > X require(X req); + + /* Optional optionalChildValue(AxiomItemDefinition supertypeReference, Class type); + + V requiredChildValue(AxiomItemDefinition supertypeReference, Class type) throws AxiomSemanticException; + + V requireValue() throws AxiomSemanticException; + + + + Context errorMessage(Supplier errorFactory); + + RuleErrorMessage error(String format, Object... arguments); + + + + Optional optionalValue(); + + Search> requireGlobal(AxiomIdentifier space, IdentifierSpaceKey key); + + Dependency> requireChild(AxiomItemDefinition required); + + Dependency requireNamespace(AxiomIdentifier name, IdentifierSpaceKey namespaceId); + + Dependency> modify(AxiomIdentifier identifierSpace, IdentifierSpaceKey identifier); + + Dependency> require(AxiomValueContext ext);*/ + } + + public interface Action { + + void apply(AxiomValueContext context) throws AxiomSemanticException; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementSource.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementSource.java deleted file mode 100644 index f7c3339dfb1..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementSource.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import java.beans.Statement; -import java.io.IOException; -import java.io.InputStream; -import java.util.Optional; -import java.util.Set; - -import org.antlr.v4.runtime.ANTLRInputStream; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.TokenStream; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.antlr.AxiomLexer; -import com.evolveum.axiom.lang.antlr.AxiomParser; -import com.evolveum.axiom.lang.antlr.AxiomParser.StatementContext; -import com.evolveum.axiom.lang.api.stmt.AxiomStatementStreamListener; - -public class AxiomStatementSource implements AxiomModelInfo { - - private final StatementContext root; - private String sourceName; - - public static AxiomStatementSource from(InputStream stream) throws IOException, AxiomSyntaxException { - return from(null, CharStreams.fromStream(stream)); - } - - public static AxiomStatementSource from(String sourceName, InputStream stream) throws IOException, AxiomSyntaxException { - return from(sourceName, CharStreams.fromStream(stream)); - } - - public static AxiomStatementSource from(String sourceName, CharStream stream) throws AxiomSyntaxException { - - AxiomLexer lexer = new AxiomLexer(stream); - AxiomParser parser = new AxiomParser(new CommonTokenStream(lexer)); - - lexer.removeErrorListeners(); - parser.removeErrorListeners(); - AxiomErrorListener errorListener = new AxiomErrorListener(sourceName); - parser.addErrorListener(errorListener); - StatementContext statement = parser.statement(); - errorListener.validate(); - return new AxiomStatementSource(sourceName, statement); - } - - private AxiomStatementSource(String sourceName, StatementContext statement) { - this.sourceName = sourceName; - this.root = statement; - } - - @Override - public String getModelName() { - return root.argument().identifier().localIdentifier().getText(); - } - - @Override - public String getNamespace() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getDescription() { - // TODO Auto-generated method stub - return null; - } - - public void stream(AxiomIdentifierResolver resolver, AxiomStatementStreamListener listener) { - stream(resolver, listener, Optional.empty()); - } - - private void stream(AxiomIdentifierResolver resolver, AxiomStatementStreamListener listener, - Optional> emitOnly) { - AxiomAntlrVisitor visitor = new AxiomAntlrVisitor<>(sourceName, resolver, listener, emitOnly.orElse(null)); - visitor.visit(root); - } -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementStreamBuilder.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementStreamBuilder.java deleted file mode 100644 index 835deb511c3..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomStatementStreamBuilder.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import java.util.Deque; -import java.util.LinkedList; -import java.util.Optional; -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; -import com.evolveum.axiom.lang.api.stmt.AxiomStatementStreamListener; -import com.evolveum.axiom.lang.api.stmt.SourceLocation; -import com.google.common.collect.Iterables; - - -public class AxiomStatementStreamBuilder implements AxiomStatementStreamListener { - - - private final ModelReactorContext context; - private final Deque queue = new LinkedList<>(); - - public AxiomStatementStreamBuilder(ModelReactorContext context, StatementTreeBuilder root) { - this.context = context; - queue.add(root); - } - - protected StatementTreeBuilder current() { - return queue.peek(); - } - - @Override - public void argument(AxiomIdentifier identifier, SourceLocation loc) { - argument0(identifier, loc); - } - - @Override - public void argument(String identifier, SourceLocation loc) { - argument0(identifier, loc); - } - - private void argument0(Object value, SourceLocation loc) { - current().setValue(value, loc); - - } - - @Override - public void startStatement(AxiomIdentifier statement, SourceLocation loc) throws AxiomSyntaxException { - Optional childDef = current().childDef(statement); - AxiomSyntaxException.check(childDef.isPresent(), loc , "Statement %s not allowed in %s", statement, current().identifier()); - queue.offerFirst(createBuilder(childDef.get(), loc)); - } - - private StatementTreeBuilder createBuilder(AxiomItemDefinition item, SourceLocation loc) { - return current().createChildNode(item.name(), loc); - } - - @Override - public void endStatement(SourceLocation loc) { - StatementTreeBuilder current = queue.poll(); - context.endStatement(current, loc); - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSyntaxException.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSyntaxException.java deleted file mode 100644 index 140873ad471..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomSyntaxException.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import java.util.Optional; - -import org.jetbrains.annotations.Nullable; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.stmt.SourceLocation; -import com.google.common.base.Strings; - -public class AxiomSyntaxException extends RuntimeException { - - - private final String source; - private final int line; - private final int charPositionInLine; - - public AxiomSyntaxException(@Nullable final String source, final int line, - final int charPositionInLine, final String message) { - this(source, line, charPositionInLine, message, null); - } - - public AxiomSyntaxException(@Nullable final String source, final int line, - final int charPositionInLine, final String message, @Nullable final Throwable cause) { - super(message, cause); - this.source = source; - this.line = line; - this.charPositionInLine = charPositionInLine; - } - - public final Optional getSource() { - return Optional.ofNullable(source); - } - - public final int getLine() { - return line; - } - - public final int getCharPositionInLine() { - return charPositionInLine; - } - - public String getFormattedMessage() { - final StringBuilder sb = new StringBuilder(getMessage()); - if (source != null) { - sb.append(" in source "); - sb.append(source); - } - if (line != 0) { - sb.append(" on line "); - sb.append(line); - if (charPositionInLine != 0) { - sb.append(" character "); - sb.append(charPositionInLine); - } - } - return sb.toString(); - } - - @Override - public String toString() { - return this.getClass().getName() + ": " + getFormattedMessage(); - } - - public static void check(boolean test, String source, int line, int posInLine, String format, Object... args) { - if(!test) { - throw new AxiomSyntaxException(source, line, posInLine, Strings.lenientFormat(format, args)); - } - - } - - public static void check(boolean present, SourceLocation loc, String format, Object... args) { - check(present, loc.getSource(), loc.getLine(), loc.getChar(),format, args); - } - -} 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 deleted file mode 100644 index 2ab7885138e..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomTypeDefinitionImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.checkerframework.checker.units.qual.K; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomBuiltIn; -import com.evolveum.axiom.lang.api.AxiomIdentifierDefinition; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; -import com.evolveum.axiom.lang.api.Identifiable; -import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.collect.Multimap; - -import static com.evolveum.axiom.lang.api.AxiomBuiltIn.Item.*; - - -public class AxiomTypeDefinitionImpl extends AbstractAxiomBaseDefinition implements AxiomTypeDefinition { - - public static final Factory FACTORY =AxiomTypeDefinitionImpl::new; - private final Map items; - private final Optional superType; - private Optional argument; - private Collection identifiers; - - public AxiomTypeDefinitionImpl(AxiomIdentifier keyword, AxiomIdentifier value, List> children, - Multimap> keywordMap) { - super(keyword, value, children, keywordMap); - 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)); - - identifiers = children(Item.IDENTIFIER_DEFINITION.name()).stream().map(idDef -> { - Set members = idDef.children(Item.ID_MEMBER.name()).stream() - .map(k -> item((AxiomIdentifier) k.value()).get()).collect(Collectors.toSet()); - AxiomIdentifier space = idDef.firstValue(ID_SPACE.name(), AxiomIdentifier.class).get(); - AxiomIdentifierDefinition.Scope scope = AxiomIdentifierDefinition.scope(idDef.firstValue(ID_SCOPE.name(), Object.class).get().toString()); - return AxiomIdentifierDefinition.from(space, scope, members); - }).collect(Collectors.toList()); - } - - @Override - public Optional argument() { - if (!argument.isPresent() && superType().isPresent()) { - return superType().get().argument(); - } - return argument; - } - - @Override - public Optional superType() { - return superType; - } - - @Override - public Map items() { - return items; - } - - @Override - public Collection identifiers() { - return identifiers; - } - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomValueContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomValueContext.java new file mode 100644 index 00000000000..ab6c32fe43e --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomValueContext.java @@ -0,0 +1,68 @@ +package com.evolveum.axiom.lang.impl; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.evolveum.axiom.lang.impl.AxiomStatementRule.ActionBuilder; + +public interface AxiomValueContext { + + void replace(AxiomValue axiomItemValue); + + default AxiomItemContext childItem(AxiomItemDefinition def) { + return childItem(def.name()); + } + + AxiomItemContext childItem(AxiomName name); + + V currentValue(); + + AxiomItemContext parent(); + + void mergeItem(AxiomItem axiomItem); + + void register(AxiomName space, Scope scope, IdentifierSpaceKey key); + + AxiomRootContext root(); + + ActionBuilder newAction(String name); + + default AxiomValueContext parentValue() { + return parent().parent(); + } + + void replaceValue(V object); + + /*V requireValue() throws AxiomSemanticException; + + AxiomItemDefinition definition(); + + AxiomValueContext createEffectiveChild(AxiomIdentifier axiomIdentifier, V value); + + Optional optionalValue(); + + void replace(Dependency> statement); + + AxiomValueContext parent(); + + void register(AxiomIdentifier space, Scope scope, IdentifierSpaceKey key); + + V requireValue(Class type); + + + void importIdentifierSpace(NamespaceContext namespaceContext); + + void exportIdentifierSpace(IdentifierSpaceKey namespace); + + void mergeItem(AxiomItem children); + + AxiomStatementRule.Context newAction(String actionName); + + AxiomStatementRule.Context modify(AxiomValueContext target, String actionName); + + void mergeEffectiveItemValues(AxiomItem axiomItem); */ + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomValueReference.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomValueReference.java new file mode 100644 index 00000000000..b92bdf4852d --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/AxiomValueReference.java @@ -0,0 +1,5 @@ +package com.evolveum.axiom.lang.impl; + +public interface AxiomValueReference { + +} 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 9e140c1e450..e80a2771859 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 @@ -1,129 +1,251 @@ package com.evolveum.axiom.lang.impl; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; - -import com.evolveum.axiom.api.AxiomIdentifier; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.meta.Inheritance; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +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.AxiomIdentifierDefinition; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.AxiomModel; import com.evolveum.axiom.lang.api.IdentifierSpaceKey; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.evolveum.axiom.reactor.Dependency; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -public enum BasicStatementRule implements StatementRule { - +public enum BasicStatementRule implements AxiomStatementRule { + /* REQUIRE_REQUIRED_ITEMS(all(),all()) { @Override - public void apply(StatementRuleContext rule) throws AxiomSemanticException { - AxiomTypeDefinition typeDef = rule.typeDefinition(); + public void apply(Context rule) throws AxiomSemanticException { + AxiomTypeDefinition typeDef = action.typeDefinition(); for(AxiomItemDefinition required : typeDef.requiredItems()) { - rule.requireChild(required).unsatisfiedMessage(() -> rule.error("%s does not have required statement %s")); - rule.apply((ctx) -> {}); + //action.requireChild(required).unsatisfiedMessage(() -> action.error("%s does not have required statement %s", action.itemDefinition().name() ,required.name())); + action.apply((ctx) -> {}); + } + } + },*/ + + MOVE_ARGUMENT_VALUE(all(),all()) { + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + if(context.isMutable()) { + Optional argument = context.typeDefinition().argument(); + if(argument.isPresent() && context.originalValue() != null) { + action.apply(ctx -> { + ctx.childItem(argument.get()).addValue(ctx.currentValue()); + ctx.replaceValue(null); + }); + } } } }, + REGISTER_TYPE(all(), types(Type.TYPE_DEFINITION)) { - COPY_ARGUMENT_VALUE(all(),all()) { + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency typeName = action.require(context.child(Item.NAME, AxiomName.class) + .map(v -> v.onlyValue().value())) + .unsatisfied(() -> action.error("Type does not have name defined.")); + action.apply(ctx -> { + ctx.register(AxiomTypeDefinition.SPACE, AxiomIdentifierDefinition.Scope.GLOBAL, AxiomTypeDefinition.identifier(typeName.get())); + }); + } + }, + REGISTER_ROOT(all(), types(Type.ROOT_DEFINITION)) { @Override - public void apply(StatementRuleContext rule) throws AxiomSemanticException { - Optional argument = rule.typeDefinition().argument(); - if(argument.isPresent() && rule.optionalValue().isPresent()) { - rule.apply(ctx -> ctx.createEffectiveChild(argument.get().name(), ctx.requireValue())); - } + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency typeName = action.require(context.child(Item.NAME, AxiomName.class) + .map(v -> v.onlyValue().value())) + .unsatisfied(() -> action.error("Type does not have name defined.")); + action.apply(ctx -> { + ctx.register(AxiomItemDefinition.ROOT_SPACE, AxiomIdentifierDefinition.Scope.GLOBAL, AxiomItemDefinition.identifier(typeName.get())); + }); } }, - REGISTER_TO_IDENTIFIER_SPACE(all(),all()) { + ITEM_VALUE_IDENTIFIER(all(),all()){ @Override - public void apply(StatementRuleContext rule) throws AxiomSemanticException { - Collection idDefs = rule.typeDefinition().identifiers(); - if(!idDefs.isEmpty()) { - rule.apply(ctx -> { - for (AxiomIdentifierDefinition idDef : idDefs) { - IdentifierSpaceKey key = keyFrom(idDef, rule); - ctx.register(idDef.space(), idDef.scope(), key); - } - - }); + public boolean isApplicableTo(AxiomItemDefinition definition) { + if(definition.identifierDefinition().isPresent()) { + return true; } + return false; } - }, - /* - * Not needed - registration is handled by identifier statement - REGISTER_TYPE(items(Item.TYPE_DEFINITION), types(Type.TYPE_DEFINITION)) { @Override - public void apply(StatementRuleContext rule) throws AxiomSemanticException { - AxiomIdentifier typeName = rule.requireValue(); - rule.apply(ctx -> ctx.registerAsGlobalItem(typeName)); + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + AxiomIdentifierDefinition idDef = context.itemDefinition().identifierDefinition().get(); + Map>> components = new HashMap<>(); + for(AxiomName key : idDef.components()) { + components.put(key, action.require(context.child(key, Object.class)) + .unsatisfied(()-> context.error("Item '%s' is required by identifier, but not defined.", key)) + .map(v -> v.onlyValue())); + } + action.apply(ctx -> { + IdentifierSpaceKey key = keyFrom(components); + ctx.register(AxiomItemDefinition.VALUE_SPACE, Scope.PARENT, key); + }); } + }, - */ EXPAND_TYPE_REFERENCE(all(), types(Type.TYPE_REFERENCE)) { @Override - public void apply(StatementRuleContext rule) throws AxiomSemanticException { - AxiomIdentifier type = rule.requireValue(); - Requirement> typeDef = rule.requireGlobalItem(AxiomTypeDefinition.IDENTIFIER_SPACE, AxiomTypeDefinition.identifier(type)); - rule.apply(ctx -> { - ctx.replace(typeDef); + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + + Dependency> typeDef = action.require(context.child(Item.NAME, AxiomName.class) + .unsatisfied(() -> action.error("type name is missing.")) + .map(v -> v.onlyValue().value()) + .flatMap(name -> + context.reference(AxiomTypeDefinition.SPACE,AxiomTypeDefinition.identifier(name)) + .notFound(() -> action.error("type '%s' was not found.", name)) + .unsatisfied(() -> action.error("Referenced type %s is not complete.", name)) + ) + ); + action.apply(ctx -> { + ctx.childItem(Item.REF_TARGET).addOperationalValue((AxiomValueReference) typeDef.get()); }); - rule.errorMessage(() -> rule.error("type '%s' was not found.", type)); } - }; -/* - ADD_SUPERTYPE(items(), types(Type.TYPE_DEFINITION)) { + }, + ITEMS_FROM_SUPERTYPE(items(Item.SUPERTYPE_REFERENCE),types(Type.TYPE_REFERENCE)) { @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()); - rule.apply((ctx) -> { - //ctx.builder().add(Item.SUPERTYPE_REFERENCE, req.get()); - }); - rule.errorMessage(() -> { - if(!req.isSatisfied()) { - return rule.error("Supertype %s is not defined", superType.get()); - } - return null; - }); - } + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency typeDef = + action.require( + context.onlyItemValue(Item.REF_TARGET, AxiomTypeDefinition.class) + .map(v -> v.asComplex().get())); + typeDef.unsatisfied(() -> action.error("Supertype is not complete.")); + action.apply(superTypeValue -> { + addFromType(typeDef.get(), superTypeValue.parentValue()); + }); } - };*/ + }, + ITEMS_FROM_MIXIN(items(Item.USES),types(Type.TYPE_REFERENCE)) { - private final Set items; - private final Set types; + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency typeDef = + action.require( + context.onlyItemValue(Item.REF_TARGET, AxiomTypeDefinition.class) + .map(v -> v.asComplex().get())); + typeDef.unsatisfied(() -> action.error("Supertype is not complete.")); + action.apply(superTypeValue -> { + addFromType(typeDef.get(), superTypeValue.parentValue()); + }); + } + }, + IMPORT_DEFAULT_TYPES(all(), types(Type.MODEL)) { + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency req = action.require(context.namespace(Item.NAMESPACE.name(), namespaceId(AxiomModel.BUILTIN_TYPES))); + req.unsatisfied(() -> action.error("Default types not found.")); + action.apply((ctx) -> { + ctx.root().importIdentifierSpace(req.get()); + }); + } + }, + EXPORT_GLOBALS_FROM_MODEL(all(), types(Type.MODEL)) { + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency namespace = action.require(context.child(Item.NAMESPACE, String.class).map(v -> v.onlyValue().value())); + action.apply(ctx -> { + ctx.root().exportIdentifierSpace(namespaceId(namespace.get())); + }); + } + }, + IMPORT_MODEL(all(),types(Type.IMPORT_DEFINITION)) { + + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency namespace = action.require(context.child(Item.NAMESPACE, String.class) + .map(item -> item.onlyValue().value()) + .flatMap(uri -> context.namespace(Item.NAMESPACE.name(), namespaceId(uri)) + .unsatisfied(() -> action.error("Namespace %s not found.", uri)) + )); + action.apply((ctx) -> { + ctx.root().importIdentifierSpace(namespace.get()); + }); + } + }, + + APPLY_AUGMENTATION(all(), types(Type.AUGMENTATION_DEFINITION)) { + + @Override + public boolean isApplicableTo(AxiomItemDefinition definition) { + return super.isApplicableTo(definition); + } - private BasicStatementRule(Set items, Set types) { + @Override + public void apply(Lookup context, ActionBuilder action) throws AxiomSemanticException { + Dependency> targetRef = action.require(context.child(Item.TARGET, AxiomName.class) + .map(v -> v.onlyValue().asComplex().get().item(Item.NAME).get().onlyValue())) + .flatMap(item -> + context.modify(AxiomTypeDefinition.SPACE, AxiomTypeDefinition.identifier((AxiomName) item.value())) + .unsatisfied(() -> action.error("Target %s not found.",item.value()) + )); + Dependency> itemDef = action.require(context.child(Item.ITEM_DEFINITION, AxiomItemDefinition.class)); + action.apply(ext -> { + for(AxiomValue item : itemDef.get().values()) { + // FIXME: Add inheritance metadata + targetRef.get().mergeItem(AxiomItem.from(Item.ITEM_DEFINITION, item)); + } + }); + } + }; + + private final Set items; + private final Set types; + + + private BasicStatementRule(Set items, Set types) { this.items = ImmutableSet.copyOf(items); this.types = ImmutableSet.copyOf(types); } - static IdentifierSpaceKey keyFrom(AxiomIdentifierDefinition idDef, StatementRuleContext ctx) { - ImmutableMap.Builder components = ImmutableMap.builder(); - for(AxiomItemDefinition cmp : idDef.components()) { - components.put(cmp.name(), ctx.requiredChildValue(cmp, Object.class)); + static IdentifierSpaceKey keyFrom(Map>> ctx) { + ImmutableMap.Builder components = ImmutableMap.builder(); + for(Entry>> entry : ctx.entrySet()) { + components.put(entry.getKey(), entry.getValue().get().value()); } return IdentifierSpaceKey.from(components.build()); } @Override public boolean isApplicableTo(AxiomItemDefinition definition) { - return (items.isEmpty() || items.contains(definition.name())) - && (types.isEmpty() || types.contains(definition.type().name())); + if (items.isEmpty() || items.contains(definition.name())) { + if(types.isEmpty()) { + return true; + } + for(AxiomName type : types) { + if(definition.typeDefinition().isSubtypeOf(type)) { + return true; + } + } + + } + return false; } - private static ImmutableSet types(AxiomTypeDefinition... types) { - ImmutableSet.Builder builder = ImmutableSet.builder(); + private static ImmutableSet types(AxiomTypeDefinition... types) { + ImmutableSet.Builder builder = ImmutableSet.builder(); for (AxiomTypeDefinition item : types) { builder.add(item.name()); } @@ -131,15 +253,32 @@ private static ImmutableSet types(AxiomTypeDefinition... types) } - private static ImmutableSet items(AxiomItemDefinition... items) { - ImmutableSet.Builder builder = ImmutableSet.builder(); + private static ImmutableSet items(AxiomItemDefinition... items) { + ImmutableSet.Builder builder = ImmutableSet.builder(); for (AxiomItemDefinition item : items) { builder.add(item.name()); } return builder.build(); } - private static ImmutableSet all() { + private static ImmutableSet all() { return ImmutableSet.of(); } + + private static IdentifierSpaceKey namespaceId(String uri) { + return IdentifierSpaceKey.of(Item.NAMESPACE.name(), uri); + } + + public static void addFromType(AxiomComplexValue source, AxiomValueContext target) { + Optional> identifiers = source.item(Item.IDENTIFIER_DEFINITION); + if(identifiers.isPresent()) { + target.mergeItem(identifiers.get()); + }// Copy Items + + Optional> itemDefs = source.item(Item.ITEM_DEFINITION); + if(itemDefs.isPresent()) { + target.mergeItem(itemDefs.get()); + } + } + } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/CompositeIdentifierSpace.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/CompositeIdentifierSpace.java new file mode 100644 index 00000000000..60472c2e11d --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/CompositeIdentifierSpace.java @@ -0,0 +1,51 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; + +class CompositeIdentifierSpace implements IdentifierSpaceHolder, NamespaceContext { + + private final Set delegates = new HashSet<>(); + private final IdentifierSpaceHolderImpl export = new IdentifierSpaceHolderImpl(Scope.GLOBAL); + + + + public IdentifierSpaceHolderImpl getExport() { + return export; + } + + public CompositeIdentifierSpace() { + delegates.add(export); + } + + @Override + public ValueContext lookup(AxiomName space, IdentifierSpaceKey key) { + for (IdentifierSpaceHolder delegate : delegates) { + ValueContext maybe = delegate.lookup(space, key); + if(maybe != null) { + return maybe; + } + } + return null; + } + + @Override + public void register(AxiomName space, Scope scope, IdentifierSpaceKey key, ValueContext context) { + export.register(space, scope, key, context); + } + + @Override + public Map> space(AxiomName space) { + throw new UnsupportedOperationException(); + } + + public void add(IdentifierSpaceHolder holder) { + delegates.add(holder); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DefaultItemDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DefaultItemDefinition.java deleted file mode 100644 index 2df8066b9a7..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DefaultItemDefinition.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.axiom.lang.impl; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; - -class DefaultItemDefinition implements AxiomItemDefinition { - - private AxiomIdentifier identifier; - private AxiomTypeDefinition type; - - public DefaultItemDefinition(AxiomIdentifier identifier, AxiomTypeDefinition type) { - super(); - this.identifier = identifier; - this.type = type; - } - - @Override - public AxiomIdentifier name() { - return identifier; - } - - @Override - public String documentation() { - return null; - } - - @Override - public AxiomTypeDefinition type() { - return type; - } - - @Override - public boolean required() { - // TODO Auto-generated method stub - return false; - } - - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DefaultTypeDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DefaultTypeDefinition.java deleted file mode 100644 index 2db4cbf9f03..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DefaultTypeDefinition.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -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.AxiomIdentifierDefinition; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; - -class DefaultTypeDefinition implements AxiomTypeDefinition { - - private AxiomIdentifier identifier; - private Map items; - - public DefaultTypeDefinition(AxiomIdentifier identifier, Map items) { - super(); - this.identifier = identifier; - this.items = items; - } - - @Override - public AxiomIdentifier name() { - return identifier; - } - - @Override - public String documentation() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Optional argument() { - return Optional.empty(); - } - - @Override - public Optional superType() { - return Optional.empty(); - } - - @Override - public Map items() { - return items; - } - - - @Override - public Collection identifiers() { - // TODO Auto-generated method stub - return null; - } - -} 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 deleted file mode 100644 index f83c3ddd823..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/DelegatedRequirement.java +++ /dev/null @@ -1,7 +0,0 @@ -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/IdentifierSpaceHolder.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/IdentifierSpaceHolder.java new file mode 100644 index 00000000000..4769db1e084 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/IdentifierSpaceHolder.java @@ -0,0 +1,17 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Map; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; + + +interface IdentifierSpaceHolder { + + void register(AxiomName space, Scope scope, IdentifierSpaceKey key, ValueContext context); + + public ValueContext lookup(AxiomName space, IdentifierSpaceKey key); + + Map> space(AxiomName space); +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/IdentifierSpaceHolderImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/IdentifierSpaceHolderImpl.java new file mode 100644 index 00000000000..083faa003f2 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/IdentifierSpaceHolderImpl.java @@ -0,0 +1,60 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; + +public class IdentifierSpaceHolderImpl implements IdentifierSpaceHolder { + + Set allowedScopes; + Map>> space = new HashMap<>(); + + public IdentifierSpaceHolderImpl(Scope first, Scope... rest) { + allowedScopes = EnumSet.of(first, rest); + } + + @Override + public void register(AxiomName space, Scope scope, IdentifierSpaceKey key, ValueContext item) { + Preconditions.checkArgument(allowedScopes.contains(scope), "Scope " + scope + " is not allowed");// TODO + // Auto-generated + // method stub + ValueContext previous = space(space).putIfAbsent(key, item); + if(previous != null) { + throw AxiomSemanticException.create(item.startLocation(), "%s: %s is already defined at %s", space.localName(),item, previous.startLocation()); + } + } + + @Override + public ValueContext lookup(AxiomName space, IdentifierSpaceKey key) { + return space(space).get(key); + } + + @Override + public Map> space(AxiomName spaceId) { + return space.computeIfAbsent(spaceId, k -> new HashMap<>()); + } + + Map>> build() { + ImmutableMap.Builder>> roots = ImmutableMap + .builder(); + for (Entry>> entry : space.entrySet()) { + ImmutableMap.Builder> space = ImmutableMap.builder(); + for (Entry> item : entry.getValue().entrySet()) { + space.put(item.getKey(), item.getValue().get()); + } + roots.put(entry.getKey(), space.build()); + } + return roots.build(); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ItemContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ItemContext.java new file mode 100644 index 00000000000..a10e43f8913 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ItemContext.java @@ -0,0 +1,122 @@ +package com.evolveum.axiom.lang.impl; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.api.stream.AxiomBuilderStreamTarget.ItemBuilder; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.reactor.Dependency; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; + +public class ItemContext extends AbstractContext> implements AxiomItemContext, Supplier>, Dependency>, ItemBuilder { + + private final AxiomName name; + Collection>> values = new ArrayList<>(); + private final AxiomItemDefinition definition; + + public ItemContext(ValueContext sourceContext, AxiomName name, AxiomItemDefinition definition, SourceLocation loc) { + super(sourceContext, loc, sourceContext); + this.name = name; + this.definition = definition; + } + + @Override + public AxiomName name() { + return name; + } + + @Override + public ValueContext startValue(Object value, SourceLocation loc) { + ValueContext valueCtx = new ValueContext<>(this, (V) value, loc); + values.add(valueCtx); + return valueCtx; + } + + @Override + public void endNode(SourceLocation loc) { + //root().applyRules(this); + } + + public AxiomTypeDefinition type() { + return definition.typeDefinition(); + } + + @Override + protected Optional childItemDef(AxiomName id) { + return type().itemDefinition(id); + } + + @Override + public boolean isSatisfied() { + for (Dependency> value : values) { + if(!(value instanceof ValueContext.ReferenceDependency)) { + if(!value.isSatisfied()) { + return false; + } + } + } + return true; + } + + @Override + public AxiomItem get() { + return AxiomItem.from(definition, Collections2.transform(values, v -> v.get())); + } + + @Override + public Exception errorMessage() { + return null; + } + + public AxiomItemDefinition definition() { + return definition; + } + + @Override + public AxiomValueContext addValue(V value) { + ValueContext ret = startValue(value, SourceLocation.runtime()); + ret.endValue(SourceLocation.runtime()); + //values.add(Dependency.immediate(AxiomItemValue.from(definition.typeDefinition(), value))); + return ret; + } + + @Override + public void addOperationalValue(AxiomValueReference value) { + Preconditions.checkState(value instanceof ValueContext.Reference); + values.add(((ValueContext.Reference) value).asDependency()); + } + + @Override + public V onlyValue() { + Preconditions.checkState(values.size() == 1); + return values.iterator().next().get().value(); + } + + @Override + public AxiomNameResolver itemResolver() { + return rootImpl().itemResolver(); + } + + @Override + public AxiomNameResolver valueResolver() { + return rootImpl().valueResolver(); + } + + public Dependency> onlyValue0() { + if(values.size() == 1) { + return values.iterator().next(); + } + return null; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/LazyValue.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/LazyValue.java new file mode 100644 index 00000000000..265c08496ba --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/LazyValue.java @@ -0,0 +1,43 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.concepts.LazyDelegate; + +class LazyValue extends LazyDelegate> implements AxiomValue { + + private final AxiomTypeDefinition type; + + public LazyValue(AxiomTypeDefinition type, Supplier> supplier) { + super(supplier::get); + this.type = type; + } + + @Override + public Optional type() { + return Optional.of(type); + } + + @Override + public V value() { + return delegate().value(); + } + + @Override + public Optional asComplex() { + return delegate().asComplex(); + } + + @Override + public Map> infraItems() { + return delegate().infraItems(); + } + +} 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 624177ca445..4cf24594ad7 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,48 +1,60 @@ 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; import java.util.Map; -import java.util.Map.Entry; 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.api.AxiomName; +import com.evolveum.axiom.api.AxiomValueFactory; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.api.stream.AxiomBuilderStreamTarget; 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.antlr.AxiomModelStatementSource; 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.IdentifierSpaceKey; -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.spi.AxiomIdentifierDefinitionImpl; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomItemDefinitionImpl; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.evolveum.axiom.lang.spi.AxiomTypeDefinitionImpl; +import com.evolveum.axiom.reactor.Dependency; +import com.evolveum.axiom.reactor.RuleReactorContext; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; import org.jetbrains.annotations.Nullable; -public class ModelReactorContext implements AxiomIdentifierResolver { +public class ModelReactorContext extends + RuleReactorContext, ValueActionImpl, RuleContextImpl> + implements AxiomNameResolver { - private static final AxiomIdentifier ROOT = AxiomIdentifier.from("root", "root"); + private static final AxiomName ROOT = AxiomName.from("root", "root"); - private static final String AXIOM_LANG_RESOURCE = "/axiom-lang.axiom"; + private static final String AXIOM_LANG_RESOURCE = "/axiom-model.axiom"; + private static final String AXIOM_BUILTIN_RESOURCE = "/axiom-base-types.axiom"; - private static final Lazy BASE_LANGUAGE_SOURCE = Lazy.from(() -> { + 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); + return AxiomModelStatementSource.from(AXIOM_LANG_RESOURCE, stream); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + private static final Lazy BASE_TYPES_SOURCE = Lazy.from(() -> { + InputStream stream = AxiomBuiltIn.class.getResourceAsStream(AXIOM_BUILTIN_RESOURCE); + try { + return AxiomModelStatementSource.from(AXIOM_BUILTIN_RESOURCE, stream); } catch (Exception e) { throw new RuntimeException(e); } @@ -74,180 +86,136 @@ 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); + reactorContext.addStatementFactory(Type.IDENTIFIER_DEFINITION.name(), AxiomIdentifierDefinitionImpl.FACTORY); + reactorContext.loadModelFromSource(BASE_LANGUAGE_SOURCE.get()); + reactorContext.loadModelFromSource(BASE_TYPES_SOURCE.get()); + } - List> rules = new ArrayList<>(); + Collection rules = new ArrayList<>(); private final AxiomSchemaContext boostrapContext; + private final Map exported = new HashMap<>(); - public ModelReactorContext(AxiomSchemaContext boostrapContext) { - this.boostrapContext = boostrapContext; - } + Map> globalItems = new HashMap<>(); - Map> globalItems = new HashMap<>(); + IdentifierSpaceHolderImpl globalSpace = new IdentifierSpaceHolderImpl(Scope.GLOBAL); - Map>> globalSpace = new HashMap<>(); + Map> typeFactories = new HashMap<>(); + List> roots = new ArrayList<>(); - Map> typeFactories = new HashMap<>(); - List outstanding = new ArrayList<>(); - List> roots = new ArrayList<>(); + public ModelReactorContext(AxiomSchemaContext boostrapContext) { + this.boostrapContext = boostrapContext; + } public AxiomSchemaContext computeSchemaContext() throws AxiomSemanticException { - boolean anyCompleted = false; - do { - anyCompleted = false; - List toCheck = outstanding; - outstanding = new ArrayList<>(); - - Iterator iterator = toCheck.iterator(); - while (iterator.hasNext()) { - StatementRuleContextImpl ruleCtx = iterator.next(); - if (ruleCtx.canProcess()) { - ruleCtx.perform(); - iterator.remove(); - anyCompleted = true; - } - } - // We add not finished items back to outstanding - outstanding.addAll(toCheck); - } while (anyCompleted); - - if (!outstanding.isEmpty()) { - failOutstanding(outstanding); - } - + compute(); return createSchemaContext(); } - private void failOutstanding(List report) { + @Override + protected void failOutstanding(Collection> outstanding) throws AxiomSemanticException { StringBuilder messages = new StringBuilder("Can not complete models, following errors occured:\n"); - for (StatementRuleContextImpl rule : report) { - RuleErrorMessage exception = rule.errorMessage(); + for (ValueActionImpl rule : outstanding) { + Exception exception = rule.errorMessage(); + messages.append("Rule: ").append(rule.name()).append("\n"); if (exception != null) { - messages.append(exception.toString()).append("\n"); - + messages.append(exception.getMessage()).append("\n"); + } + for (Dependency req : rule.dependencies()) { + if (!req.isSatisfied() && req.errorMessage() != null) { + messages.append(req.errorMessage().getMessage()).append("\n"); + } } } - throw new AxiomSemanticException(messages.toString()); + throw new AxiomSemanticException(null, messages.toString()); } private AxiomSchemaContext createSchemaContext() { - ImmutableMap.Builder>> roots = ImmutableMap.builder(); - for (Entry>> entry: globalSpace.entrySet()) { - ImmutableMap.Builder> space = ImmutableMap.builder(); - for (Entry> item : entry.getValue().entrySet()) { - space.put(item.getKey(), item.getValue().asLazy().get()); - } - roots.put(entry.getKey(), space.build()); + return new AxiomSchemaContextImpl(globalSpace.build()); + } - } - return new AxiomSchemaContextImpl(roots.build()); + public IdentifierSpaceHolderImpl space() { + return globalSpace; + } + @Override + protected void addOutstanding(ValueActionImpl action) { + super.addOutstanding(action); } - public void registerGlobalItem(AxiomIdentifier typeName, StatementContextImpl context) { - globalItems.put(typeName, context); + public void addRules(AxiomStatementRule... rules) { + for (AxiomStatementRule statementRule : rules) { + this.rules.add(new RuleContextImpl(statementRule)); + } } + public ModelReactorContext loadModelFromSource(AxiomModelStatementSource source) { + SourceContext sourceCtx = new SourceContext(this, source, source.imports(), new CompositeIdentifierSpace()); + source.stream(new AxiomBuilderStreamTarget(sourceCtx), Optional.empty()); + return this; + } - public void registerGlobal(AxiomIdentifier space, IdentifierSpaceKey key, StatementContextImpl item) { - StatementContextImpl previous = globalSpace(space).putIfAbsent(key, item); - if(previous != null) { - throw new AxiomSemanticException(item.startLocation() + Strings.lenientFormat("Item %s in %s identifier space is already defined at %s", item.optionalValue(), space, previous.startLocation())); + @Override + public AxiomName resolveIdentifier(@Nullable String prefix, @NotNull String localName) { + if (Strings.isNullOrEmpty(prefix)) { + return AxiomName.axiom(localName); } + return null; } - private Map> globalSpace(AxiomIdentifier spaceId) { - return globalSpace.computeIfAbsent(spaceId, k -> new HashMap<>()); + public void addStatementFactory(AxiomName statementType, AxiomValueFactory factory) { + typeFactories.put(statementType, factory); } - public Requirement> requireGlobalItem(AxiomIdentifier space, IdentifierSpaceKey key) { - return (Requirement) Requirement.retriableDelegate(() -> { - StatementContextImpl maybeCtx = globalSpace(space).get(key); - if (maybeCtx != null) { - return maybeCtx.asRequirement(); + public AxiomValueFactory typeFactory(AxiomTypeDefinition statementType) { + Optional current = Optional.of(statementType); + do { + @SuppressWarnings("unchecked") + AxiomValueFactory maybe = (AxiomValueFactory) typeFactories.get(current.get().name()); + if (maybe != null) { + return maybe; } - return null; - }); + current = current.get().superType(); + } while (current.isPresent()); + return AxiomValueFactory.defaultFactory(); } - public void addOutstanding(StatementRuleContextImpl rule) { - outstanding.add(rule); + public void exportIdentifierSpace(IdentifierSpaceKey namespace, IdentifierSpaceHolder localSpace) { + exported(namespace).add(localSpace); } - void endStatement(StatementTreeBuilder cur, SourceLocation loc) throws AxiomSemanticException { - if (cur instanceof StatementContextImpl) { - StatementContextImpl current = (StatementContextImpl) cur; - for (StatementRule statementRule : rules) { - if (statementRule.isApplicableTo(current.definition())) { - current.addRule(statementRule); - } - } - } + private CompositeIdentifierSpace exported(IdentifierSpaceKey namespace) { + return exported.computeIfAbsent(namespace, k -> new CompositeIdentifierSpace()); } - public void addRules(StatementRule... rules) { - for (StatementRule statementRule : rules) { - this.rules.add(statementRule); - } + public Dependency namespace(AxiomName name, IdentifierSpaceKey namespaceId) { + return Dependency.orNull(exported.get(namespaceId)); } - public void loadModelFromSource(AxiomStatementSource statementSource) { - statementSource.stream(this, new AxiomStatementStreamBuilder(this, new Root())); + @Override + protected AxiomSemanticException createException() { + return null; } @Override - public AxiomIdentifier resolveStatementIdentifier(@Nullable String prefix, @NotNull String localName) { - return AxiomIdentifier.axiom(localName); + protected Collection rulesFor(ValueContext context) { + // TODO: Add smart filters if neccessary + return rules; } - private class Root implements StatementTreeBuilder { - - @Override - public void setValue(Object value) { - // NOOP - } - - @Override - public Optional childDef(AxiomIdentifier statement) { - return boostrapContext.getRoot(statement); - } - - @Override - public StatementTreeBuilder createChildNode(AxiomIdentifier identifier, SourceLocation loc) { - StatementContextImpl ret = new StatementContextImpl<>(ModelReactorContext.this, null, - childDef(identifier).get(), loc); - roots.add(ret); - return ret; - } - - @Override - public AxiomIdentifier identifier() { - return ROOT; - } - - @Override - public void setValue(Object value, SourceLocation loc) { - - } + public Optional rootDefinition(AxiomName statement) { + return boostrapContext.getRoot(statement); } - public void addStatementFactory(AxiomIdentifier statementType, Factory factory) { - typeFactories.put(statementType, factory); + public void applyRuleDefinitions(ValueContext valueContext) { + addActionsFor(valueContext); } - public Factory typeFactory(AxiomTypeDefinition statementType) { - Optional current = Optional.of(statementType); - do { - Factory maybe = typeFactories.get(current.get().name()); - if (maybe != null) { - return maybe; - } - current = current.get().superType(); - } while (current.isPresent()); - - return (Factory) AxiomStatementImpl.factory(); + public void register(AxiomName space, Scope scope, IdentifierSpaceKey key, ValueContext context) { + globalSpace.register(space, scope, key, context); } + } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/NamespaceContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/NamespaceContext.java new file mode 100644 index 00000000000..3b9ce951c7c --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/NamespaceContext.java @@ -0,0 +1,5 @@ +package com.evolveum.axiom.lang.impl; + +public interface NamespaceContext { + +} 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 deleted file mode 100644 index d0fedce26e7..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Requirement.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.Optional; -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 { - - boolean isSatisfied(); - public T get(); - - public RuleErrorMessage errorMessage(); - - public static Requirement unsatisfied() { - return new Unsatified<>(); - } - - public static Requirement immediate(T value) { - return new Immediate<>(value); - } - - public static Requirement from(Supplier supplier) { - return new Suppliable<>(supplier); - } - - public static abstract class Abstract implements Requirement { - - - private Supplier errorMessage; - - @Override - public Requirement unsatisfiedMessage(Supplier unsatisfiedMessage) { - errorMessage = unsatisfiedMessage; - return this; - } - - - @Override - public RuleErrorMessage errorMessage() { - if(errorMessage != null) { - return errorMessage.get(); - } - return null; - } - } - - - public static final class Immediate extends Abstract { - - private final V value; - - @Override - public boolean isSatisfied() { - return true; - } - - public Immediate(V value) { - super(); - this.value = value; - } - - @Override - public V get() { - return value; - } - - } - - public static final class Suppliable extends Abstract { - - private final Supplier value; - - @Override - public boolean isSatisfied() { - return value.get() != null; - } - - public Suppliable(Supplier value) { - super(); - this.value = value; - } - - @Override - public V get() { - return value.get(); - } - - } - - public static final class Unsatified extends Abstract { - - @Override - public boolean isSatisfied() { - return false; - } - - @Override - public V get() { - throw new IllegalStateException("Requirement not satisfied"); - } - } - - public abstract class Delegated extends Abstract { - - 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); - } - - default Requirement unsatisfiedMessage(Supplier unsatisfiedMessage) { - return this; - } - static Requirement from(Optional maybe) { - if(maybe.isPresent()) { - return immediate(maybe.get()); - } - return unsatisfied(); - } -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleContextImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleContextImpl.java new file mode 100644 index 00000000000..7950b5cf1ce --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleContextImpl.java @@ -0,0 +1,37 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.Collection; + +import com.evolveum.axiom.reactor.Rule; + +public class RuleContextImpl implements Rule, ValueActionImpl> { + + private final AxiomStatementRule delegate; + + public RuleContextImpl(AxiomStatementRule delegate) { + super(); + this.delegate = delegate; + } + + @Override + public boolean applicableTo(ValueContext context) { + return delegate.isApplicableTo(context.parent().definition()); + } + + @Override + public Collection> applyTo(ValueContext context) { + ValueActionImpl actionBuilder = context.addAction(delegate.name()); + delegate.apply(context.getLookup(), actionBuilder); + return actionBuilder.build(); + } + + @Override + public String toString() { + return delegate.toString(); + } + + public AxiomStatementRule delegate() { + return delegate; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleErrorMessage.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleErrorMessage.java index 067a6e25cb8..b064f5d093f 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleErrorMessage.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/RuleErrorMessage.java @@ -1,6 +1,6 @@ package com.evolveum.axiom.lang.impl; -import com.evolveum.axiom.lang.api.stmt.SourceLocation; +import com.evolveum.axiom.concepts.SourceLocation; import com.google.common.base.Strings; public class RuleErrorMessage { diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/SourceContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/SourceContext.java new file mode 100644 index 00000000000..b0d90df893b --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/SourceContext.java @@ -0,0 +1,120 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomValueFactory; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.antlr.AxiomModelStatementSource; +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.api.stream.AxiomBuilderStreamTarget.ValueBuilder; +import com.evolveum.axiom.reactor.Dependency; +import com.google.common.base.Preconditions; + +class SourceContext extends ValueContext implements AxiomRootContext, ValueBuilder { + + private static final AxiomName ROOT = AxiomName.from("root", "root"); + + private final ModelReactorContext context; + private final AxiomModelStatementSource source; + private final Map items = new HashMap(); + private final CompositeIdentifierSpace globalSpace; + private Map imports; + + public SourceContext(ModelReactorContext context, AxiomModelStatementSource source, Map imports, CompositeIdentifierSpace space) { + super(SourceLocation.runtime(), space); + this.context = context; + this.source = source; + this.globalSpace = space; + this.imports = imports; + } + + @Override + public AxiomName name() { + return ROOT; + } + + @Override + public ItemContext startItem(AxiomName identifier, SourceLocation loc) { + return items.computeIfAbsent(identifier, id -> createItem(id, loc)); + } + + @Override + public Optional childItemDef(AxiomName statement) { + return context.rootDefinition(statement); + } + + @Override + protected SourceContext rootImpl() { + return this; + } + + @Override + public void endValue(SourceLocation loc) { + // NOOP + } + + @Override + public void register(AxiomName space, Scope scope, IdentifierSpaceKey key, ValueContext context) { + globalSpace.register(space, scope, key, context); + this.context.register(space, scope, key, context); + } + + @Override + public ValueContext lookup(AxiomName space, IdentifierSpaceKey key) { + return globalSpace.lookup(space, key); + } + + public AxiomValueFactory factoryFor(AxiomTypeDefinition type) { + return context.typeFactory(type); + } + + public void applyRuleDefinitions(ValueContext valueContext) { + context.applyRuleDefinitions(valueContext); + } + + public Dependency requireNamespace(AxiomName name, IdentifierSpaceKey namespaceId) { + return Dependency.retriableDelegate(() -> { + return context.namespace(name, namespaceId); + }); + } + + @Override + public void importIdentifierSpace(NamespaceContext namespaceContext) { + Preconditions.checkArgument(namespaceContext instanceof IdentifierSpaceHolder); + globalSpace.add((IdentifierSpaceHolder) namespaceContext); + } + + @Override + public void exportIdentifierSpace(IdentifierSpaceKey namespaceId) { + context.exportIdentifierSpace(namespaceId, globalSpace.getExport()); + } + + @Override + public AxiomNameResolver itemResolver() { + return axiomAsConditionalDefault().or((prefix, localName) -> { + String namespace = imports.get(prefix); + if(namespace != null) { + return AxiomName.from(namespace, localName); + } + return null; + }); + } + + @Override + public AxiomNameResolver valueResolver() { + return AxiomNameResolver.BUILTIN_TYPES.or((prefix, localName) -> { + String namespace = imports.get(prefix); + if(namespace != null) { + return AxiomName.from(namespace, localName); + } + return null; + }); + } +} 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 deleted file mode 100644 index bb3f92e0686..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.Optional; -import java.util.function.Supplier; - -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.IdentifierSpaceKey; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; -import com.evolveum.axiom.lang.api.AxiomIdentifierDefinition.Scope; - -public interface StatementContext { - - V requireValue() throws AxiomSemanticException; - - AxiomItemDefinition definition(); - - void registerAsGlobalItem(AxiomIdentifier typeName) throws AxiomSemanticException; - - StatementContext createEffectiveChild(AxiomIdentifier axiomIdentifier, V value); - - Optional optionalValue(); - - void replace(Requirement> statement); - - StatementContext parent(); - - void register(AxiomIdentifier space, Scope scope, IdentifierSpaceKey key); - - V requireValue(Class type); - -} 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 deleted file mode 100644 index 103d4075323..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.function.Supplier; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.concepts.Lazy; -import com.evolveum.axiom.concepts.Optionals; -import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; -import com.evolveum.axiom.lang.api.AxiomIdentifierDefinition.Scope; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; -import com.evolveum.axiom.lang.api.IdentifierSpaceKey; -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; -import com.google.common.collect.Multimap; - -public class StatementContextImpl implements StatementContext, StatementTreeBuilder { - - - - private final ModelReactorContext reactor; - private final AxiomItemDefinition definition; - private final StatementContextImpl parent; - - private SourceLocation startLocation; - private SourceLocation endLocation; - private SourceLocation valueLocation; - private Requirement> result; - - - StatementContextImpl(ModelReactorContext reactor, StatementContextImpl parent, AxiomItemDefinition definition, SourceLocation loc) { - this.parent = parent; - this.reactor = reactor; - this.definition = definition; - this.startLocation = loc; - AxiomStatementBuilder builder = new AxiomStatementBuilder<>(definition.name(), reactor.typeFactory(definition.type())); - this.result = new StatementContextResult<>(definition, builder); - } - - @Override - public void setValue(Object value) { - mutableResult().setValue((V) value); - } - - private StatementContextResult mutableResult() { - if(result instanceof StatementContextResult) { - return (StatementContextResult) result; - } - return null; - } - - boolean isChildAllowed(AxiomIdentifier child) { - return definition.type().item(child).isPresent(); - } - - - @SuppressWarnings("rawtypes") - @Override - public StatementContextImpl createChildNode(AxiomIdentifier identifier, SourceLocation loc) { - StatementContextImpl childCtx = new StatementContextImpl(reactor, this, childDef(identifier).get(), loc); - mutableResult().add(childCtx); - return childCtx; - } - - @Override - public String toString() { - return "Context(" + definition.name() + ")"; - } - - @Override - public Optional childDef(AxiomIdentifier child) { - return definition.type().item(child); - } - - @Override - public AxiomIdentifier identifier() { - return definition.name(); - } - - @Override - public V requireValue() throws AxiomSemanticException { - return AxiomSemanticException.checkNotNull(mutableResult().value(), definition, "must have argument specified."); - } - - @Override - public AxiomItemDefinition definition() { - return definition; - } - - @Override - public void registerAsGlobalItem(AxiomIdentifier typeName) throws AxiomSemanticException { - reactor.registerGlobalItem(typeName, this); - } - - - @Override - public StatementContext createEffectiveChild(AxiomIdentifier axiomIdentifier, V value) { - StatementContextImpl child = createChildNode(axiomIdentifier, null); - child.setValue(value); - return child; - } - - - public void registerRule(StatementRuleContextImpl rule) { - mutableResult().addRule(rule); - this.reactor.addOutstanding(rule); - } - - public SourceLocation startLocation() { - return startLocation; - } - - ModelReactorContext reactor() { - return reactor; - } - - public Optional> firstChild(AxiomItemDefinition child) { - return Optionals.first(children(child.name())); - } - - private Collection> children(AxiomIdentifier identifier) { - return (Collection) mutableResult().get(identifier); - } - - public void addRule(StatementRule statementRule) throws AxiomSemanticException { - statementRule.apply(new StatementRuleContextImpl(this,statementRule)); - } - - @Override - public Optional optionalValue() { - return Optional.ofNullable(mutableResult().value()); - } - - @Override - public void replace(Requirement> supplier) { - this.result = (Requirement) supplier; - } - - @Override - public StatementContext parent() { - return parent; - } - - @Override - public void setValue(Object value, SourceLocation loc) { - setValue(value); - 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(); - } - - - @Override - public void register(AxiomIdentifier space, Scope scope, IdentifierSpaceKey key) { - switch (scope) { - case GLOBAL: - registerGlobal(space, key); - break; - case LOCAL: - registerLocalParent(space, key); - break; - default: - throw new IllegalStateException("Unsupporrted scope"); - } - } - - private void registerLocalParent(AxiomIdentifier space, IdentifierSpaceKey key) { - // TODO Auto-generated method stub - - } - - private void registerGlobal(AxiomIdentifier space, IdentifierSpaceKey key) { - reactor.registerGlobal(space, key, this); - } - - @Override - public V requireValue(Class type) { - if(result instanceof StatementContextResult) { - return type.cast(((StatementContextResult) result).value()); - } - return null; - } - - public Requirement> requireChild(AxiomItemDefinition required) { - return Requirement.retriableDelegate(() -> { - if(mutableResult() != null) { - return (Requirement) firstChild(required).map(StatementContextImpl::asRequirement).orElse(null); - } - Optional> maybe = result.get().first(required); - - return Requirement.from(maybe); - }); - } - -} 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 deleted file mode 100644 index d973b27607a..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementContextResult.java +++ /dev/null @@ -1,75 +0,0 @@ -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); - } - - @Override - public RuleErrorMessage errorMessage() { - // TODO Auto-generated method stub - return null; - } -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRule.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRule.java deleted file mode 100644 index 61ecad414b0..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRule.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import com.evolveum.axiom.lang.api.AxiomItemDefinition; - -public interface StatementRule { - - String name(); - - boolean isApplicableTo(AxiomItemDefinition definition); - - void apply(StatementRuleContext rule) throws AxiomSemanticException; - -} 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 deleted file mode 100644 index 21d42969931..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContext.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.Optional; -import java.util.function.Supplier; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.AxiomTypeDefinition; -import com.evolveum.axiom.lang.api.IdentifierSpaceKey; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; - -public interface StatementRuleContext { - - Optional optionalChildValue(AxiomItemDefinition supertypeReference, Class type); - - V requiredChildValue(AxiomItemDefinition supertypeReference, Class type) throws AxiomSemanticException; - - V requireValue() throws AxiomSemanticException; - - StatementRuleContext apply(StatementRuleContext.Action action); - - StatementRuleContext errorMessage(Supplier errorFactory); - - RuleErrorMessage error(String format, Object... arguments); - - public interface Action { - - void apply(StatementContext context) throws AxiomSemanticException; - } - - AxiomTypeDefinition typeDefinition(); - - Optional optionalValue(); - - Requirement> requireGlobalItem(AxiomIdentifier space, IdentifierSpaceKey key); - - Requirement> requireChild(AxiomItemDefinition required); - -} 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 deleted file mode 100644 index 3f3a23f6093..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementRuleContextImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -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.IdentifierSpaceKey; -import com.evolveum.axiom.lang.api.stmt.AxiomStatement; -import com.sun.net.httpserver.Authenticator.Result; -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(AxiomIdentifier space, - IdentifierSpaceKey key) { - return requirement(context.reactor().requireGlobalItem(space, key)); - } - - 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 { - StatementContext ctx = context.firstChild(supertypeReference).get(); - return (V) ctx.requireValue(type); - } - - @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 Optional optionalValue() { - return context.optionalValue(); - } - - @Override - public RuleErrorMessage error(String format, Object... arguments) { - return RuleErrorMessage.from(context.startLocation(), format, arguments); - } - - @Override - public Requirement> requireChild(AxiomItemDefinition required) { - return context.requireChild(required); - } - -} 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 deleted file mode 100644 index 37154cda505..00000000000 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/StatementTreeBuilder.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.evolveum.axiom.lang.impl; - -import java.util.Optional; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.lang.api.AxiomItemDefinition; -import com.evolveum.axiom.lang.api.stmt.SourceLocation; - -public interface StatementTreeBuilder { - - void setValue(Object value); - - Optional childDef(AxiomIdentifier statement); - - AxiomIdentifier identifier(); - - void setValue(Object value, SourceLocation loc); - - StatementTreeBuilder createChildNode(AxiomIdentifier identifier, SourceLocation loc); - - -} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ValueActionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ValueActionImpl.java new file mode 100644 index 00000000000..6b0c2aad5f7 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ValueActionImpl.java @@ -0,0 +1,137 @@ +package com.evolveum.axiom.lang.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.evolveum.axiom.reactor.Dependency; +import com.evolveum.axiom.reactor.DependantAction; +import com.google.common.collect.ImmutableList; +public class ValueActionImpl implements AxiomStatementRule.ActionBuilder, DependantAction { + + private final ValueContext context; + private final String rule; + private final List> dependencies = new ArrayList<>(); + private AxiomStatementRule.Action action; + private Supplier errorReport = () -> null; + private boolean applied = false; + private Exception error; + + public ValueActionImpl(ValueContext context, String rule) { + this.context = context; + this.rule = rule; + } + + + + @Override + public > X require(X req) { + this.dependencies.add(req); + return req; + } + + @Override + public ValueActionImpl apply(AxiomStatementRule.Action action) { + this.action = action; + context.dependsOnAction(this); + return this; + } + + public boolean canProcess() { + for (Dependency requirement : dependencies) { + if (!requirement.isSatisfied()) { + return false; + } + } + return true; + } + + @Override + public void apply() throws AxiomSemanticException { + if(!applied && error == null) { + this.action.apply(context); + this.applied = true; + } + } + + public boolean isApplied() { + return applied; + } + + + @Override + public Exception errorMessage() { + if(error != null) { + return error; + } + + return errorReport.get(); + } + + @Override + public String toString() { + return context.toString() + ":" + rule; + } + + + @Override + public boolean successful() { + return applied; + } + + public Collection> requirements() { + return dependencies; + } + + @Override + public void fail(Exception e) throws AxiomSemanticException { + this.error = new AxiomSemanticException(context.startLocation(), e.getClass().getName() + " " + e.getMessage(), e); + } + + + @Override + public Collection> dependencies() { + return dependencies; + } + + @Override + public Optional error() { + return Optional.empty(); + } + + public boolean isDefined() { + return action != null; + } + + public Collection> build() { + if(action != null) { + return ImmutableList.of(this); + } + return Collections.emptyList(); + } + + + + @Override + public Dependency> require(AxiomValueContext ext) { + return require((Dependency>) ext); + } + + + + @Override + public AxiomSemanticException error(String message, Object... arguments) { + return AxiomSemanticException.create(context.startLocation(), message, arguments); + } + + public String name() { + return rule; + } + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ValueContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ValueContext.java new file mode 100644 index 00000000000..38434baf16a --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/ValueContext.java @@ -0,0 +1,446 @@ +package com.evolveum.axiom.lang.impl; + +import com.evolveum.axiom.lang.api.IdentifierSpaceKey; +import com.evolveum.axiom.lang.impl.AxiomStatementRule.ActionBuilder; +import com.evolveum.axiom.lang.impl.AxiomStatementRule.Lookup; +import com.evolveum.axiom.api.stream.AxiomBuilderStreamTarget.ItemBuilder; +import com.evolveum.axiom.api.stream.AxiomBuilderStreamTarget.ValueBuilder; +import com.evolveum.axiom.reactor.Dependency; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.function.Supplier; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.AxiomValueBuilder; +import com.evolveum.axiom.api.meta.Inheritance; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition.Scope; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; + +public class ValueContext extends AbstractContext> implements AxiomValueContext, ValueBuilder, Dependency> { + + private Dependency> result; + private final LookupImpl lookup = new LookupImpl(); + private final V originalValue; + private final Collection> dependencies = new HashSet<>(); + private AxiomValue lazyValue; + public ReferenceDependency referenceDependency = new ReferenceDependency(); + public Reference reference = new Reference(); + + public ValueContext(SourceLocation loc, IdentifierSpaceHolder space) { + super(null, loc, space); + result = new Result(null,null); + originalValue = null; + } + + public ValueContext(ItemContext itemContext, V value, SourceLocation loc) { + super(itemContext, loc, AxiomIdentifierDefinition.Scope.LOCAL); + originalValue = value; + result = new Result(parent().type(), value); + lazyValue = new LazyValue<>(itemDefinition().typeDefinition(),() -> get()); + } + + @Override + public AxiomName name() { + return parent().name(); + } + + public LookupImpl getLookup() { + return lookup; + } + + @Override + public Optional childItemDef(AxiomName statement) { + return parent().type().itemDefinition(statement); + } + + @Override + public ItemContext startItem(AxiomName identifier, SourceLocation loc) { + return mutable().getOrCreateItem(Inheritance.adapt(parent().name(), identifier), loc); + } + + @Override + public void endValue(SourceLocation loc) { + rootImpl().applyRuleDefinitions(this); + } + + protected Result mutable() { + Preconditions.checkState(result instanceof ValueContext.Result, "Result is not mutable."); + return (Result) result; + } + + @Override + public boolean isSatisfied() { + return result.isSatisfied(); + } + + @Override + public AxiomValue get() { + if(isMutable()) { + result = Dependency.immediate(result.get()); + } + return result.get(); + } + + @Override + public Exception errorMessage() { + return null; + } + + private ItemContext mutableItem(Supplier> supplier) { + Preconditions.checkState(supplier instanceof ItemContext); + return (ItemContext) supplier; + } + + public AxiomItemDefinition itemDefinition() { + return parent().definition(); + } + + public ValueActionImpl addAction(String name) { + return new ValueActionImpl<>(this, name); + } + + protected ItemContext createItem(AxiomName id, SourceLocation loc) { + return new ItemContext<>(this, id ,childItemDef(id).get(), loc); + } + + private class Result implements Dependency> { + + AxiomTypeDefinition type; + AxiomValueBuilder builder; + private V value; + + public Result(AxiomTypeDefinition type, V value) { + this.type = type; + this.value = value; + builder = AxiomValueBuilder.create(type, null); + } + + ItemContext getOrCreateItem(AxiomName identifier, SourceLocation loc) { + return mutableItem(builder.get(identifier, id -> { + ItemContext item = createItem(id, loc); + addDependency(item); + return item; + })); + } + + Dependency> getItem(AxiomName item) { + Supplier> maybeItem = builder.get(item); + if(maybeItem == null) { + return null; + } + if(maybeItem instanceof Dependency) { + return (Dependency) maybeItem; + } + return Dependency.immediate((AxiomItem) maybeItem.get()); + } + + @Override + public boolean isSatisfied() { + return Dependency.allSatisfied(dependencies); + } + + @Override + public AxiomValue get() { + builder.setValue(value); + builder.setFactory(rootImpl().factoryFor(type)); + return builder.get(); + } + + @Override + public Exception errorMessage() { + return null; + } + + } + + void addDependency(Dependency action) { + dependencies.add(action); + } + + @Override + public void replace(AxiomValue axiomItemValue) { + this.result = Dependency.immediate((AxiomValue) axiomItemValue); + } + + @Override + public ItemContext childItem(AxiomName name) { + return (ItemContext) mutable().getOrCreateItem(Inheritance.adapt(parent().name(), name), SourceLocation.runtime()); + } + + @Override + public V currentValue() { + if(result instanceof ValueContext.Result) { + return ((ValueContext.Result) result).value; + } + return get().value(); + } + + @Override + public void mergeItem(AxiomItem axiomItem) { + ItemContext item = startItem(axiomItem.name(), SourceLocation.runtime()); + for(AxiomValue value : axiomItem.values()) { + ValueContext valueCtx = item.startValue(value.value(),SourceLocation.runtime()); + valueCtx.replace(value); + valueCtx.endValue(SourceLocation.runtime()); + } + item.endNode(SourceLocation.runtime()); + } + + @Override + public void register(AxiomName space, Scope scope, IdentifierSpaceKey key) { + register(space, scope, key, this); + } + + + + @Override + public ActionBuilder newAction(String name) { + return new ValueActionImpl(this, name); + } + + @Override + public AxiomRootContext root() { + return parent().rootImpl(); + } + + public void dependsOnAction(ValueActionImpl action) { + addDependency(action); + } + + public Dependency.Search> requireChild(AxiomName item) { + return Dependency.retriableDelegate(() -> { + if(result instanceof ValueContext.Result) { + return ((ValueContext.Result) result).getItem(item); + } + return Dependency.from(result.get().asComplex().get().item(item)); + + }); + } + + @Override + public void replaceValue(V object) { + mutable().value = object; + } + + public boolean isMutable() { + return result instanceof ValueContext.Result; + } + + @Override + public String toString() { + return new StringBuffer().append(parent().definition().name().localName()) + .append(" ") + .append(originalValue != null ? originalValue : "") + .toString(); + } + + private class LookupImpl implements Lookup { + + @Override + public AxiomItemDefinition itemDefinition() { + return parent().definition(); + } + + @Override + public Dependency namespace(AxiomName name, IdentifierSpaceKey namespaceId) { + return rootImpl().requireNamespace(name, namespaceId); + } + + Optional resolveChildDef(AxiomItemDefinition name) { + Optional exactDef = childItemDef(name.name()); + if(exactDef.isPresent()) { + Optional localDef = childItemDef(parent().name().localName(name.name().localName())); + if(localDef.isPresent() && localDef.get() instanceof AxiomItemDefinition.Inherited) { + return localDef; + } + } + return exactDef; + } + + @Override + public Dependency> child(AxiomItemDefinition definition, Class valueType) { + return requireChild(Inheritance.adapt(parent().name(), definition)); + } + + @Override + public Dependency> child(AxiomName definition, Class valueType) { + return requireChild(Inheritance.adapt(parent().name(), definition)); + } + + @Override + public Dependency> onlyItemValue(AxiomItemDefinition definition, Class valueType) { + return Dependency.retriableDelegate(() -> { + Dependency> item; + if(result instanceof ValueContext.Result) { + item = ((ValueContext.Result) result).getItem(definition.name()); + } else { + item = result.flatMap(v -> Dependency.from(v.asComplex().get().item(definition))); + } + if(item instanceof ItemContext) { + return ((ItemContext) item).onlyValue0(); + } else if (item == null) { + return null; + } + return item.map(i -> i.onlyValue()); + }); + } + + @Override + public Dependency> modify(AxiomName space, IdentifierSpaceKey key) { + return (Dependency.retriableDelegate(() -> { + ValueContext maybe = lookup(space, key); + if(maybe != null) { + //maybe.addDependency(this); + return Dependency.immediate(maybe); + } + return null; + })); + } + + @Override + public Dependency.Search> global(AxiomName space, + IdentifierSpaceKey key) { + return Dependency.retriableDelegate(() -> { + ValueContext maybe = lookup(space, key); + if(maybe != null) { + return (Dependency) maybe; + } + return null; + }); + } + + @Override + public Dependency.Search> reference(AxiomName space, + IdentifierSpaceKey key) { + return Dependency.retriableDelegate(() -> { + ValueContext maybe = lookup(space, key); + if(maybe != null) { + return Dependency.immediate(maybe.reference); + } + return null; + }); + } + + @Override + public Dependency.Search> namespaceValue(AxiomName space, + IdentifierSpaceKey key) { + return Dependency.retriableDelegate(() -> { + ValueContext maybe = lookup(space, key); + if(maybe != null) { + return (Dependency) maybe; + } + return null; + }); + } + + @Override + public Dependency finalValue() { + return map(v -> v.value()); + } + + @Override + public V currentValue() { + return ValueContext.this.currentValue(); + } + + @Override + public V originalValue() { + return originalValue; + } + + @Override + public boolean isMutable() { + return ValueContext.this.isMutable(); + } + + @Override + public Lookup parentValue() { + return parent().parent().getLookup(); + } + + @Override + public AxiomSemanticException error(String message, Object... arguments) { + return AxiomSemanticException.create(startLocation(), message, arguments); + } + } + + @Override + public AxiomNameResolver itemResolver() { + return (prefix, localName) -> { + if(Strings.isNullOrEmpty(prefix)) { + AxiomName localNs = AxiomName.local(localName); + Optional childDef = childItemDef(localNs); + if(childDef.isPresent()) { + return Inheritance.adapt(parent().name(), childDef.get()); + } + ItemContext parent = parent(); + while(parent != null) { + AxiomName parentNs = AxiomName.from(parent.name().namespace(), localName); + if(childItemDef(parentNs).isPresent()) { + return parentNs; + } + parent = parent.parent().parent(); + } + } + return rootImpl().itemResolver().resolveIdentifier(prefix, localName); + }; + } + + public AxiomValue lazyValue() { + return lazyValue; + } + + @Override + public AxiomNameResolver valueResolver() { + return rootImpl().valueResolver(); + } + + final class Reference implements AxiomValueReference { + + public ReferenceDependency asDependency() { + return referenceDependency; + } + + } + + final class ReferenceDependency implements Dependency> { + + @Override + public boolean isSatisfied() { + return ValueContext.this.isSatisfied(); + } + + @Override + public AxiomValue get() { + return lazyValue; + } + + @Override + public Exception errorMessage() { + // TODO Auto-generated method stub + return null; + } + } + + @Override + public Optional infraItemDef(AxiomName item) { + throw new UnsupportedOperationException("Infra Items not yet supported for schema"); + } + + @Override + public ItemBuilder startInfra(AxiomName identifier, SourceLocation loc) { + throw new UnsupportedOperationException("Infra Items not yet supported for schema"); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AbstractBaseDefinition.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AbstractBaseDefinition.java new file mode 100644 index 00000000000..2a2501f3f2f --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AbstractBaseDefinition.java @@ -0,0 +1,33 @@ +package com.evolveum.axiom.lang.spi; + +import java.util.Map; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.ComplexValueImpl; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.schema.AxiomNamedDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; + +public class AbstractBaseDefinition extends ComplexValueImpl implements AxiomNamedDefinition { + + private final AxiomName name; + private final String documentation; + + public AbstractBaseDefinition(AxiomTypeDefinition type, Map> items, Map> infraItems) { + super(type, items, infraItems); + name = (AxiomName) item(Item.NAME).get().onlyValue().value(); + documentation = item(Item.DOCUMENTATION).map(i -> i.onlyValue().value().toString()).orElse(null); // + } + + @Override + public AxiomName name() { + return name; + } + + @Override + public String documentation() { + return documentation; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomIdentifierDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomIdentifierDefinitionImpl.java new file mode 100644 index 00000000000..cf54528c88a --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomIdentifierDefinitionImpl.java @@ -0,0 +1,42 @@ +package com.evolveum.axiom.lang.spi; + +import java.util.Collection; +import java.util.Map; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.ComplexValueImpl; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; +import com.google.common.collect.ImmutableList; + +public class AxiomIdentifierDefinitionImpl extends ComplexValueImpl implements AxiomIdentifierDefinition { + + public static final AxiomComplexValue.Factory FACTORY = AxiomIdentifierDefinitionImpl::new; + private final Collection components; + + public AxiomIdentifierDefinitionImpl(AxiomTypeDefinition axiomItemDefinition, Map> items, Map> infraItems) { + super(axiomItemDefinition, items, infraItems); + + ImmutableList.Builder components = ImmutableList.builder(); + for (AxiomValue val : this.item(Item.ID_MEMBER.name()).get().values()) { + components.add(val.value()); + } + this.components = components.build(); + } + + @Override + public Collection components() { + return components; + } + + public static AxiomIdentifierDefinition from(AxiomValue value) { + if (value instanceof AxiomIdentifierDefinition) { + return (AxiomIdentifierDefinition) value; + } + return new AxiomIdentifierDefinitionImpl(value.type().get(), null, value.asComplex().get().itemMap()); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomItemDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomItemDefinitionImpl.java new file mode 100644 index 00000000000..59e3612ac1e --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomItemDefinitionImpl.java @@ -0,0 +1,77 @@ +package com.evolveum.axiom.lang.spi; + +import java.util.Map; +import java.util.Optional; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; + +public class AxiomItemDefinitionImpl extends AbstractBaseDefinition implements AxiomItemDefinition { + + public static final AxiomComplexValue.Factory FACTORY = AxiomItemDefinitionImpl::new; + private final AxiomValue valueType; + private final Optional> minOccurs; + private Optional identifierDef; + + public AxiomItemDefinitionImpl(AxiomTypeDefinition axiomItemDefinition, Map> items, Map> infraItems) { + super(axiomItemDefinition, items, infraItems); + this.valueType = require(asComplex().get().onlyValue(AxiomTypeDefinition.class,Item.TYPE_REFERENCE, Item.REF_TARGET)); + this.identifierDef = asComplex().get().onlyValue(AxiomIdentifierDefinition.class, Item.IDENTIFIER_DEFINITION).map(v -> AxiomIdentifierDefinitionImpl.from(v)); + minOccurs = this.item(Item.MIN_OCCURS.name()); + } + + @Override + public AxiomTypeDefinition definingType() { + return null; + } + + @Override + public boolean operational() { + return false; + } + + @Override + public AxiomTypeDefinition typeDefinition() { + return AxiomTypeDefinitionImpl.from(valueType.asComplex().get()); + } + + @Override + public boolean required() { + return minOccurs() > 0; + } + + @Override + public int minOccurs() { + return minOccurs.map(i -> Integer.parseInt(i.onlyValue().value())).orElse(0); + } + + @Override + public int maxOccurs() { + // FIXME: Return real value + return Integer.MAX_VALUE; + } + + @Override + public String toString() { + return AxiomItemDefinition.toString(this); + } + + @Override + public Optional identifierDefinition() { + return identifierDef; + } + + public static AxiomItemDefinition from(AxiomValue value) { + if(value instanceof AxiomItemDefinition) { + return (AxiomItemDefinition) value; + } + return new AxiomItemDefinitionImpl(value.type().get(), null, value.asComplex().get().itemMap()); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomItemStreamTreeBuilder.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomItemStreamTreeBuilder.java new file mode 100644 index 00000000000..46a1b5baabd --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomItemStreamTreeBuilder.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.spi; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.Optional; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.stream.AxiomItemStream; +import com.evolveum.axiom.concepts.SourceLocation; +import com.google.common.base.Preconditions; + + +public class AxiomItemStreamTreeBuilder implements AxiomItemStream.Target { + + private final Deque queue = new LinkedList<>(); + + public AxiomItemStreamTreeBuilder(ValueBuilder root) { + queue.add(root); + } + + protected V offer(V builder) { + queue.offerFirst(builder); + return builder; + } + + protected Builder current() { + return queue.peek(); + } + + protected Builder poll() { + return queue.poll(); + } + + private ItemBuilder item(Builder node) { + Preconditions.checkState(node instanceof ItemBuilder); + return (ItemBuilder) node; + } + + private ValueBuilder value(Builder node) { + Preconditions.checkState(node instanceof ValueBuilder); + return (ValueBuilder) node; + } + + @Override + public void startValue(Object value, SourceLocation loc) { + queue.offerFirst(item(current()).startValue(value, loc)); + } + + @Override + public void endValue(SourceLocation loc) { + value(poll()).endValue(loc); + } + + @Override + public void startItem(AxiomName item, SourceLocation loc) { + Optional childDef = value(current()).childDef(item); + AxiomSyntaxException.check(childDef.isPresent(), loc , "Item %s not allowed in %s", item, current().name()); + offer(value(current()).startItem(item, loc)); + } + + @Override + public void endItem(SourceLocation loc) { + item(poll()).endNode(loc); + } + + private interface Builder { + AxiomName name(); + } + + public interface ItemBuilder extends Builder { + ValueBuilder startValue(Object value, SourceLocation loc); + void endNode(SourceLocation loc); + + } + + public interface ValueBuilder extends Builder { + Optional childDef(AxiomName statement); + ItemBuilder startItem(AxiomName identifier, SourceLocation loc); + void endValue(SourceLocation loc); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomNameResolver.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomNameResolver.java new file mode 100644 index 00000000000..43405befbc7 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomNameResolver.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.spi; + +import java.util.Optional; +import java.util.Set; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.meta.Inheritance; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; + +import org.jetbrains.annotations.Nullable; + +public interface AxiomNameResolver { + + final AxiomNameResolver AXIOM_DEFAULT_NAMESPACE = defaultNamespace(AxiomName.AXIOM_NAMESPACE); + final Set BUILTINS = ImmutableSet.of("string","boolean","uri", "int", "binary", "dateTime"); + final AxiomNameResolver BUILTIN_TYPES = (prefix, localName) -> { + if((prefix == null || prefix.isEmpty()) && BUILTINS.contains(localName)) { + return AxiomName.axiom(localName); + } + return null; + }; + + final AxiomNameResolver NULL_RESOLVER = (p, n) -> null; + + AxiomName resolveIdentifier(@Nullable String prefix, @NotNull String localName); + + static AxiomNameResolver defaultNamespace(String namespace) { + return (prefix, localName) -> Strings.isNullOrEmpty(prefix) ? AxiomName.from(namespace, localName) : null; + } + + static AxiomNameResolver nullResolver() { + return NULL_RESOLVER; + } + + default AxiomNameResolver or(AxiomNameResolver next) { + return (prefix, localName) -> { + AxiomName maybe = this.resolveIdentifier(prefix, localName); + if (maybe != null) { + return maybe; + } + return next.resolveIdentifier(prefix, localName); + }; + } + + static AxiomNameResolver defaultNamespaceFromType(AxiomTypeDefinition type) { + return (prefix, localName) -> { + if(Strings.isNullOrEmpty(prefix)) { + AxiomName localNs = AxiomName.local(localName); + Optional childDef = type.itemDefinition(localNs); + if(childDef.isPresent()) { + return Inheritance.adapt(type.name(), childDef.get()); + } + } + return null; + }; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomSemanticException.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomSemanticException.java new file mode 100644 index 00000000000..d51281e6809 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomSemanticException.java @@ -0,0 +1,29 @@ +package com.evolveum.axiom.lang.spi; + +import com.evolveum.axiom.concepts.SourceLocation; +import com.google.common.base.Strings; + +public class AxiomSemanticException extends RuntimeException { + + private final SourceLocation source; + + public AxiomSemanticException(SourceLocation source, String message, Throwable cause) { + super(source + ":" + message, cause); + this.source = source; + } + + public AxiomSemanticException(SourceLocation source, String message) { + this(source, message, null); + } + + public static void check(boolean check, SourceLocation start, String format, Object... arguments) { + if(!check) { + throw create(start, format, arguments); + } + } + + public static AxiomSemanticException create(SourceLocation start, String format, Object... arguments) { + return new AxiomSemanticException(start, Strings.lenientFormat(format, arguments)); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomSyntaxException.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomSyntaxException.java new file mode 100644 index 00000000000..fbf8efef811 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomSyntaxException.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.spi; + +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.evolveum.axiom.concepts.SourceLocation; +import com.google.common.base.Strings; + +public class AxiomSyntaxException extends RuntimeException { + + + private final SourceLocation source; + + public AxiomSyntaxException(@Nullable final SourceLocation source, String message, @Nullable final Throwable cause) { + super(message, cause); + this.source = source; + } + + public AxiomSyntaxException(SourceLocation source, String message) { + this(source, message, null); + } + + public final Optional getSource() { + return Optional.ofNullable(source); + } + + public String getFormattedMessage() { + final StringBuilder sb = new StringBuilder(); + if (source != null) { + sb.append(source); + } + return sb.append(getMessage()).toString(); + } + + @Override + public String toString() { + return this.getClass().getName() + ": " + getFormattedMessage(); + } + + public static void check(boolean test, SourceLocation source, String format, Object... args) { + if(!test) { + throw new AxiomSyntaxException(source, Strings.lenientFormat(format, args)); + } + } + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomTypeDefinitionImpl.java b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomTypeDefinitionImpl.java new file mode 100644 index 00000000000..0b12de00f83 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/lang/spi/AxiomTypeDefinitionImpl.java @@ -0,0 +1,91 @@ +package com.evolveum.axiom.lang.spi; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.meta.Inheritance; +import com.evolveum.axiom.api.schema.AxiomIdentifierDefinition; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; + + +public class AxiomTypeDefinitionImpl extends AbstractBaseDefinition implements AxiomTypeDefinition { + + public static final AxiomComplexValue.Factory FACTORY = AxiomTypeDefinitionImpl::new; + private final Map itemDefinitions; + private final Optional superType; + private final Optional argument; + private final Collection identifiers; + + public AxiomTypeDefinitionImpl(AxiomTypeDefinition def, Map> keywordMap, Map> infraItems) { + super(def, keywordMap, infraItems); + + ImmutableMap.Builder builder = ImmutableMap.builder(); + Optional> itemDef = item(Item.ITEM_DEFINITION.name()); + if(itemDef.isPresent()) { + supplyAll(name(),builder, itemDef.get().values()); + } + itemDefinitions = builder.build(); + + superType = onlyValue(AxiomTypeDefinition.class,Item.SUPERTYPE_REFERENCE, Item.REF_TARGET).map(v -> from(v.asComplex().get())); + + + argument = this.item(Item.ARGUMENT.name()).flatMap(v -> itemDefinition(v.onlyValue().value())); + identifiers = Collections2.transform((this.item(Item.IDENTIFIER_DEFINITION).map(v -> v.values()).orElse(Collections.emptyList())), + AxiomIdentifierDefinitionImpl::from); + } + + public static AxiomTypeDefinition from(AxiomComplexValue value) { + if(value instanceof AxiomTypeDefinition) { + return (AxiomTypeDefinition) value; + } + return new AxiomTypeDefinitionImpl(value.type().get(), null, value.asComplex().get().itemMap()); + } + + @Override + public Optional> item(AxiomName name) { + return super.item(name); + } + + @Override + public Optional argument() { + if (!argument.isPresent() && superType().isPresent()) { + return superType().get().argument(); + } + return argument; + } + + @Override + public Optional superType() { + return superType; + } + + @Override + public Map itemDefinitions() { + return itemDefinitions; + } + + @Override + public Collection identifierDefinitions() { + return identifiers; + } + + private void supplyAll(AxiomName type, Builder builder, + Collection> values) { + for(AxiomValue v : values) { + AxiomItemDefinition val = AxiomItemDefinitionImpl.from(v); + AxiomName name = Inheritance.adapt(type, val.name()); + builder.put(name, val); + } + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/AbstractDependency.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/AbstractDependency.java new file mode 100644 index 00000000000..07639641950 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/AbstractDependency.java @@ -0,0 +1,24 @@ +package com.evolveum.axiom.reactor; + +import java.util.function.Supplier; + +public abstract class AbstractDependency implements Dependency { + + + private Supplier errorMessage; + + @Override + public Dependency unsatisfied(Supplier unsatisfiedMessage) { + errorMessage = unsatisfiedMessage; + return this; + } + + + @Override + public Exception errorMessage() { + if(errorMessage != null) { + return errorMessage.get(); + } + return null; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Action.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Action.java new file mode 100644 index 00000000000..8f4f00d130a --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Action.java @@ -0,0 +1,39 @@ +package com.evolveum.axiom.reactor; + +import java.util.Optional; + +public interface Action extends Dependency { + + void apply(); + + @Override + default boolean isSatisfied() { + return successful(); + } + + @Override + default Void get() { + return null; + } + + boolean successful(); + + /** + * Returns true if action can be applied. + * + * Return false if action application of action failed with exception, which is non-retriable. + * + * @return + */ + boolean canApply(); + + /** + * + * @param e Exception which occured during call of {@link #apply()} + * @throws E If action specific exception if failed critically and all computation should be stopped. + */ + void fail(Exception e) throws E; + + Optional error(); + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/ActionState.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/ActionState.java new file mode 100644 index 00000000000..3c3ff852ba9 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/ActionState.java @@ -0,0 +1,31 @@ +package com.evolveum.axiom.reactor; + +public enum ActionState { + NOT_READY(false, false, false), + APPLICABLE(true, false, false), + APPLIED(false, true, false), + FAILED(false, false, true); + + private ActionState(boolean satisfied, boolean applied, boolean failed) { + this.satisfied = satisfied; + this.applied = applied; + this.failed = failed; + } + + private final boolean satisfied; + private final boolean applied; + private final boolean failed; + + boolean canApply() { + return satisfied; + } + + boolean applied() { + return applied; + } + + boolean failed() { + return failed; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/BaseReactorContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/BaseReactorContext.java new file mode 100644 index 00000000000..ff1f6d9a13f --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/BaseReactorContext.java @@ -0,0 +1,81 @@ +package com.evolveum.axiom.reactor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; + +public abstract class BaseReactorContext> { + + + private Collection outstanding = new ArrayList<>(); + + public void compute() throws E { + boolean anyActionCompleted = false; + do { + anyActionCompleted = false; + Collection toCheck = takeOutstanding(); + Iterator iterator = toCheck.iterator(); + while (iterator.hasNext()) { + A action = iterator.next(); + try { + if (action.canApply()) { + action.apply(); + } + } catch (Exception e) { + action.fail(e); + } + if(action.successful()) { + iterator.remove(); + anyActionCompleted = true; + } + } + // We add not finished items back to outstanding + addOutstanding(toCheck); + } while (anyActionCompleted); + + if (!outstanding.isEmpty()) { + failOutstanding(outstanding); + } + } + + protected void fail(A action, Exception e) throws E { + action.fail(e); + } + + protected void failOutstanding(Collection outstanding) throws E { + E common = createException(); + for (A action : outstanding) { + if(!action.canApply()) { + addSuppresed(common, action); + + } + } + throw common; + } + + protected void addOutstanding(A action) { + outstanding.add(action); + } + + protected void addOutstanding(Collection action) { + outstanding.addAll(action); + } + + protected void addSuppresed(E common, A action) { + Optional error = action.error(); + if(error.isPresent()) { + common.addSuppressed(error.get()); + } + } + + protected abstract E createException(); + + private Collection takeOutstanding() { + Collection ret = outstanding; + outstanding = new ArrayList<>(); + return ret; + } + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Deffered.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Deffered.java similarity index 70% rename from infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Deffered.java rename to infra/axiom/src/main/java/com/evolveum/axiom/reactor/Deffered.java index 2e6bfdf74b6..4a2c0fe9376 100644 --- a/infra/axiom/src/main/java/com/evolveum/axiom/lang/impl/Deffered.java +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Deffered.java @@ -1,21 +1,21 @@ -package com.evolveum.axiom.lang.impl; +package com.evolveum.axiom.reactor; import java.util.Optional; import com.google.common.base.Preconditions; -class Deffered extends Requirement.Delegated { +class Deffered extends DelegatedDependency { private Object ret; - Deffered(Requirement delegate) { + Deffered(Dependency delegate) { ret = delegate; } @Override - Requirement delegate() { - if(ret instanceof Requirement) { - return (Requirement) ret; + Dependency delegate() { + if(ret instanceof Dependency) { + return (Dependency) ret; } return null; } diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/DelegatedDependency.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/DelegatedDependency.java new file mode 100644 index 00000000000..da124cfbdad --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/DelegatedDependency.java @@ -0,0 +1,30 @@ +package com.evolveum.axiom.reactor; + +import com.google.common.base.Preconditions; + +public abstract class DelegatedDependency extends AbstractDependency { + + abstract Dependency delegate(); + + @Override + public boolean isSatisfied() { + return delegate().isSatisfied(); + } + + @Override + public boolean isRequired() { + return delegate().isRequired(); + } + + @Override + public T get() { + Preconditions.checkState(isSatisfied(), "Requirement was not satisfied"); + return delegate().get(); + } + + @Override + public Exception errorMessage() { + Exception maybe = super.errorMessage(); + return maybe != null ? maybe : delegate().errorMessage(); + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/DependantAction.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/DependantAction.java new file mode 100644 index 00000000000..5c6cd773c22 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/DependantAction.java @@ -0,0 +1,15 @@ +package com.evolveum.axiom.reactor; + +import java.util.Collection; + +public interface DependantAction extends Action { + + Collection> dependencies(); + + @Override + default boolean canApply() { + return Dependency.allSatisfied(dependencies()); + } + + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Dependency.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Dependency.java new file mode 100644 index 00000000000..838bdd63ef5 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Dependency.java @@ -0,0 +1,163 @@ +package com.evolveum.axiom.reactor; + +import java.util.Collection; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + + +public interface Dependency { + + default boolean isRequired() { + return true; + } + boolean isSatisfied(); + public T get(); + + public Exception errorMessage(); + + public static Dependency unsatisfied() { + return new Unsatified<>(); + } + + public static Dependency immediate(T value) { + return new Immediate<>(value); + } + + public static Dependency optional(Dependency dependency) { + return new OptionalDep(dependency); + } + + public static Dependency from(Supplier supplier) { + return new Suppliable<>(supplier); + } + + public static Dependency deffered(Dependency original) { + return new Deffered<>(original); + } + + default Dependency unsatisfied(Supplier unsatisfiedMessage) { + return this; + } + + interface Search extends Dependency { + + default Dependency.Search notFound(Supplier unsatisfiedMessage) { + return this; + } + + } + + public static final class Immediate extends AbstractDependency { + + private final V value; + + @Override + public boolean isSatisfied() { + return true; + } + + public Immediate(V value) { + super(); + this.value = value; + } + + @Override + public V get() { + return value; + } + + } + + public static final class Suppliable extends AbstractDependency { + + private final Supplier value; + + @Override + public boolean isSatisfied() { + return value.get() != null; + } + + public Suppliable(Supplier value) { + super(); + this.value = value; + } + + @Override + public V get() { + return value.get(); + } + + } + + public static final class Unsatified extends AbstractDependency { + + @Override + public boolean isSatisfied() { + return false; + } + + @Override + public V get() { + throw new IllegalStateException("Requirement not satisfied"); + } + } + + public final class OptionalDep extends DelegatedDependency { + + private final Dependency delegate; + + public OptionalDep(Dependency delegate) { + super(); + this.delegate = delegate; + } + + @Override + public boolean isRequired() { + return false; + } + + @Override + Dependency delegate() { + return delegate; + } + + } + + static Search retriableDelegate(Supplier> lookup) { + return new RetriableDependency(lookup); + } + + static Dependency from(Optional maybe) { + if(maybe.isPresent()) { + return immediate(maybe.get()); + } + return unsatisfied(); + } + static Dependency orNull(T value) { + if(value != null) { + return immediate(value); + } + return null; + } + static boolean allSatisfied(Collection> outstanding) { + for (Dependency dependency : outstanding) { + if(!dependency.isSatisfied()) { + return false; + } + } + return true; + } + + default Dependency map(Function map) { + return new MapDependency<>(this, map); + } + + default Dependency flatMap(Function> map) { + return new FlatMapDependency<>(this, map); + } + static Dependency of(T from) { + return Dependency.immediate(from); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/FlatMapDependency.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/FlatMapDependency.java new file mode 100644 index 00000000000..f32430b0d4c --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/FlatMapDependency.java @@ -0,0 +1,25 @@ +package com.evolveum.axiom.reactor; + +import java.util.function.Function; + +public class FlatMapDependency extends DelegatedDependency { + + private Dependency delegate; + private Function> mapping; + + public FlatMapDependency(Dependency delegate, Function> mapping) { + super(); + this.delegate = delegate; + this.mapping = mapping; + } + + @Override + Dependency delegate() { + if(mapping != null && delegate.isSatisfied()) { + delegate = mapping.apply((I) delegate.get()); + mapping = null; + } + return delegate; + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/MapDependency.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/MapDependency.java new file mode 100644 index 00000000000..4e5e01b52a8 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/MapDependency.java @@ -0,0 +1,26 @@ +package com.evolveum.axiom.reactor; + +import java.util.function.Function; + +public class MapDependency extends DelegatedDependency { + + private Dependency delegate; + private Function mapping; + + public MapDependency(Dependency delegate, Function mapping) { + super(); + this.delegate = delegate; + this.mapping = mapping; + } + + @Override + Dependency delegate() { + return delegate; + } + + @Override + public O get() { + return mapping.apply(delegate.get()); + } + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/RetriableDependency.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/RetriableDependency.java new file mode 100644 index 00000000000..e70b096c575 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/RetriableDependency.java @@ -0,0 +1,48 @@ +package com.evolveum.axiom.reactor; + +import java.util.function.Supplier; + +import com.evolveum.axiom.reactor.Dependency.Search; + +public final class RetriableDependency extends DelegatedDependency implements Search { + + private Object maybeDelegate; + private Supplier notFound; + + public RetriableDependency(Supplier> lookup) { + maybeDelegate = lookup; + } + + @Override + Dependency delegate() { + if(maybeDelegate instanceof Dependency) { + return (Dependency) maybeDelegate; + } + if(maybeDelegate instanceof Supplier) { + Dependency result = ((Supplier>) maybeDelegate).get(); + if(result != null) { + maybeDelegate = result; + return (Dependency) result; + } + } + return Dependency.unsatisfied(); + } + + @Override + public Search notFound(Supplier unsatisfiedMessage) { + notFound = unsatisfiedMessage; + return this; + } + + @Override + public Exception errorMessage() { + if(maybeDelegate instanceof Supplier && notFound != null) { + return notFound.get(); + } + Exception maybeFound = super.errorMessage(); + if(maybeFound == null && maybeDelegate instanceof Dependency) { + maybeFound = ((Dependency)maybeDelegate).errorMessage(); + } + return maybeFound; + } +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Rule.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Rule.java new file mode 100644 index 00000000000..64ed6831550 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/Rule.java @@ -0,0 +1,11 @@ +package com.evolveum.axiom.reactor; + +import java.util.Collection; + +public interface Rule> { + + boolean applicableTo(C context); + + Collection applyTo(C context); + +} diff --git a/infra/axiom/src/main/java/com/evolveum/axiom/reactor/RuleReactorContext.java b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/RuleReactorContext.java new file mode 100644 index 00000000000..4ec8f506977 --- /dev/null +++ b/infra/axiom/src/main/java/com/evolveum/axiom/reactor/RuleReactorContext.java @@ -0,0 +1,17 @@ +package com.evolveum.axiom.reactor; + +import java.util.Collection; + +public abstract class RuleReactorContext, R extends Rule> extends BaseReactorContext{ + + protected abstract Collection rulesFor(C context); + + protected void addActionsFor(C context) { + Collection rules = rulesFor(context); + for (R rule : rules) { + if(rule.applicableTo(context)) { + addOutstanding(rule.applyTo(context)); + } + } + } +} diff --git a/infra/axiom/src/test/resources/axiom-base-types.axiom b/infra/axiom/src/main/resources/axiom-base-types.axiom similarity index 80% rename from infra/axiom/src/test/resources/axiom-base-types.axiom rename to infra/axiom/src/main/resources/axiom-base-types.axiom index 93f34d62719..f431b80c95c 100644 --- a/infra/axiom/src/test/resources/axiom-base-types.axiom +++ b/infra/axiom/src/main/resources/axiom-base-types.axiom @@ -8,12 +8,15 @@ model axiom-base-types { - namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; + namespace "https://schema.evolveum.com/ns/axiom/model"; version "0.1.0"; + type string; + type boolean; type uri; type int; type binary; type dateTime; - + + type anyType; } diff --git a/infra/axiom/src/main/resources/axiom-lang.axiom b/infra/axiom/src/main/resources/axiom-lang.axiom deleted file mode 100644 index a0f5d8f300b..00000000000 --- a/infra/axiom/src/main/resources/axiom-lang.axiom +++ /dev/null @@ -1,212 +0,0 @@ -model axiom-lang { - - root model { - documentation """ - Axiom Model - """; - type AxiomModel; - } - - type AxiomImportDeclaration { - argument prefix; - - item prefix { - type AxiomIdentifier; - } - - item uri { - type string; - } - } - - type AxiomModel { - extends AxiomBaseDefinition; - - item import { - type AxiomImportDeclaration; - } - - item root { - type AxiomRootDefinition; - } - - item type { - documentation """ - Type Declaration - """; - type AxiomTypeDefinition; - } - - item namespace { - type string; - } - - item version { - type string; - } - - // TODO move to prism schema; consider renaming to objectType? - item object { - type PrismObjectDefinition; - } - - // TODO move to prism schema; consider renaming to containerType? - item container { - type PrismContainerDefinition; - } - - // TODO move to prism schema; consider renaming to referenceType? - item reference { - type PrismReferenceDefinition; - } - - // TODO move to prism schema - item item { - type PrismItemDefinition; - } - } - - type AxiomRootDefinition { - extends AxiomItemDefinition; - identifier name { - scope global; - space AxiomRootDefinition; - } - } - - type AxiomBaseDefinition { - argument name; - - item name { - type AxiomIdentifier; - } - - item documentation { - type string; - } - - item since { - type SemanticVersion; - } - } - - type AxiomIdentifierDefinition { - argument key; - item key { - type AxiomIdentifier; - minOccurs "1"; - } - item scope { - type string; - minOccurs "1"; - } - item space { - type AxiomIdentifier; - minOccurs "1"; - } - } - - type AxiomTypeDefinition { - extends AxiomBaseDefinition; - - identifier name { - scope global; - space AxiomTypeDefinition; - } - - item argument { - type AxiomIdentifier; - } - - item extends { - type AxiomTypeReference; - } - - item identifier { - type AxiomIdentifierDefinition; - } - - // TODO move to prism schema - item object { - type boolean; - } - - // TODO move to prism schema - item container { - type boolean; - } - - // TODO move to prism schema - item objectReference { - type boolean; - } - - // TODO reconsider this - strictly speaking this is part of "global type+item definition combo" - item itemName { - type AxiomIdentifier; - } - - item item { - type AxiomItemDefinition; - } - } - - type AxiomIdentifier { - } - - type AxiomItemDefinition { - extends AxiomBaseDefinition; - - identifier name { - space AxiomItemDefinition; - scope local; - } - - item type { - type AxiomTypeReference; - } - item minOccurs { - type string; - } - item maxOccurs { - type string; - } - } - - type AxiomReference { - item targetType { - type AxiomTypeReference; - } - } - - type AxiomTypeReference; - - // "Type library" (temporary) - - type string; - type boolean; - - type SemanticVersion { - extends string; - } - - // TODO move to prism schema; probably should be prism:ObjectDefinition - type PrismObjectDefinition { - extends AxiomTypeDefinition; - } - - // TODO move to prism schema; probably should be prism:ContainerDefinition - type PrismContainerDefinition { - extends AxiomTypeDefinition; - } - - // TODO move to prism schema; probably should be prism:ReferenceDefinition - type PrismReferenceDefinition { - extends AxiomTypeDefinition; - } - - // TODO move to prism schema; probably should be prism:ItemDefinition - type PrismItemDefinition { - extends AxiomItemDefinition; - } -} diff --git a/infra/axiom/src/main/resources/axiom-model.axiom b/infra/axiom/src/main/resources/axiom-model.axiom new file mode 100644 index 00000000000..9d87f758ccb --- /dev/null +++ b/infra/axiom/src/main/resources/axiom-model.axiom @@ -0,0 +1,377 @@ +model axiom-model { + + namespace "https://schema.evolveum.com/ns/axiom/model"; + + root model { + documentation """ + Declaration of Axiom model. + """; + type AxiomModel; + } + + // Axiom Baseline Types + + type AxiomName { + documentation """ + Qualified name. Consists of namespace and local name. + """; + } + + type SemanticVersion { + supertype string; + } + + type ItemCompleteness; // FIXME: Add definition + type ValueSignificance; // FIXME: Add definition + type ValueMetadata; + // Axiom Infra Model + + type AxiomItem { + item name { + type AxiomName; + } + item definition { + type AxiomItemDefinition; + operational true; + } + item value { + type AxiomValue; + } + + item completeness { + type ItemCompleteness; + } + } + + type AxiomValue { + item type { + type AxiomTypeReference; + } + item significance { + type ValueSignificance; + } + item metadata { + type ValueMetadata; + } + } + + type AxiomSimpleValue { + supertype AxiomValue; + argument value; + item value { + type anyType; + } + } + + type AxiomComplexValue { + supertype AxiomValue; + item item { + type AxiomItem; + } + } + + // Axiom Schema + + type AxiomBaseDefinition { + argument name; + + item name { + type AxiomName; + } + + item documentation { + type string; + } + + item since { + type SemanticVersion; + } + + item status { + type string; + documentation """ + Status of definition, could be experimental, stable, deprecated. + """; + } + } + + type AxiomIdentifierDefinition { + argument key; + item key { + type AxiomName; + minOccurs "1"; + } + } + + type AxiomTypeDefinition { + supertype AxiomBaseDefinition; + documentation """ + Definition of value type. + + Defines type name, type supertype and set of value items. + """; + + item argument { + type AxiomName; + documentation """ + Name of item, which is target of short-form value declaration. + """; + minOccurs 0; + maxOccurs 1; + } + + // FIXME: should be inherit + item supertype { + type AxiomTypeReference; + documentation """ + Reference to super type definition, which is subtyped by this type. + """; + minOccurs 0; + maxOccurs 1; + } + + // FIXME: should be use + item uses { + type AxiomTypeReference; + documentation """ + Reference to mixin, from which this type definition reuses item definitions. + """; + minOccurs 0; + } + + // TODO reconsider this - strictly speaking this is part of "global type+item definition combo" + item itemName { + type AxiomName; + } + + item item { + type AxiomItemDefinition; + identifier name; + documentation """ + Definition of child items + """; + minOccurs 0; + } + + } + + type AxiomItemDefinition { + supertype AxiomBaseDefinition; + + item identifier { + type AxiomIdentifierDefinition; + documentation """ + Definition of value identifier for this item. + """; + minOccurs 0; + } + + item type { + type AxiomTypeReference; + documentation """ + Value type. + item minOccurs { + + All values must be instances or referenced type or it's subtypes. + """; + minOccurs 1; + maxOccurs 1; + } + + item minOccurs { + type string; + documentation """ + Declares minimum count of values required for this item. + """; + } + + item maxOccurs { + type string; + documentation """ + Declares maximum possible count of values for this item. + """; + } + + item operational { + type boolean; + documentation """ + Marks if data are operational (provided by system). + """; + } + + item inherited { + type boolean; + operational true; + } + + item default { + type anyType; // This should somehow be same type as value of type + documentation """ + Declares default value for item. + """; + minOccurs 0; + maxOccurs 1; + } + + item infra { + type InfraSpecification; + //default { + // value AxiomValue; + // item AxiomItem; + //} + } + } + + type AxiomRootDefinition { + supertype AxiomItemDefinition; + documentation """ + Root item definition. + + Root item is item which can be root of the serialized / deserialized document. + """; + } + + // Axiom Model? + + type AxiomModel { + supertype AxiomBaseDefinition; + + item namespace { + type string; + documentation """ + Namespace of model. + """; + } + + item version { + type SemanticVersion; + documentation """ + Version of model. + + Model versioning follows Semantic Versioning 2.0. + """; + } + + item import { + type AxiomImportDeclaration; + documentation """ + Declaration of model imports. + + Type definitions from imported models are referencable and + visible to all definition contained in this model. + """; + } + + item root { + documentation """ + Defines allowed root type for value serialization / deserialization. + """; + type AxiomRootDefinition; + } + + item type { + type AxiomTypeDefinition; + documentation """ + Declaration of a type. This type is visible to models + importing parent model namespace. + """; + } + + item mixin { + type AxiomMixinDefinition; + documentation """ + Declaration of mixin, a set of reusable item + definitions, which can be used in type definitions. + """; + } + + // FIXME: should be augmentation + item augmentation { + type AxiomAugmentationDefinition; + documentation """ + Declaration of augmentation. Augmentation adds new items + to already existing type, which can be defined in other + models (namespaces). + """; + } + + item metadata { + type AxiomMetadataDefinition; + } + } + + type AxiomImportDeclaration { + argument namespace; + + item prefix { + type AxiomName; + } + + item namespace { + type string; + } + } + + type InfraSpecification { + item value { + type AxiomTypeReference; + // default AxiomValue; + } + item item { + type AxiomTypeReference; + // default AxiomItem; + } + } + + type AxiomTypeReference { + documentation """ + Reference to type definition. + + Referenced type definition is referenced by it's fully qualified + (namespace and local name). + """; + argument name; + + item name { + type AxiomName; + documentation """ + Name of referenced type definition. + The value must be name of existing type definition, which is + available (visible) to model using this type reference. + """; + minOccurs 1; + maxOccurs 1; + } + item target { + type AxiomTypeDefinition; + documentation """ + Referenced Type Definition. + + The value is resolved operational definition of referenced type. + """; + minOccurs 0; + maxOccurs 1; + operational true; + } + } + + type AxiomMixinDefinition { + supertype AxiomTypeDefinition; + } + + type AxiomAugmentationDefinition { + supertype AxiomMixinDefinition; + item target { + type AxiomTypeReference; + } + } + + type AxiomMetadataDefinition { + supertype AxiomTypeDefinition; + //item itemName { + // type AxiomName; + //} + // TODO: Add limits for specific types + } + +} diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/AbstractReactorTest.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/AbstractReactorTest.java new file mode 100644 index 00000000000..a165f0d97a0 --- /dev/null +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/AbstractReactorTest.java @@ -0,0 +1,45 @@ +package com.evolveum.axiom.lang.test; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.lang.antlr.AxiomAntlrStatementSource; +import com.evolveum.axiom.lang.antlr.AxiomModelStatementSource; +import com.evolveum.axiom.lang.api.AxiomBuiltIn; +import com.evolveum.axiom.lang.impl.ModelReactorContext; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; +import com.evolveum.midpoint.tools.testng.AbstractUnitTest; + +public abstract class AbstractReactorTest extends AbstractUnitTest { + + private static final String COMMON_DIR_PATH = "src/test/resources/"; + + protected static AxiomSchemaContext parseFile(String name) throws AxiomSyntaxException, FileNotFoundException, IOException { + return parseInputStream(name, new FileInputStream(COMMON_DIR_PATH + name)); + } + + protected static AxiomSchemaContext parseInputStream(String name, InputStream stream) throws AxiomSyntaxException, FileNotFoundException, IOException { + return parseInputStream(name, stream, AxiomBuiltIn.Item.MODEL_DEFINITION); + } + + protected static AxiomSchemaContext parseInputStream(String name, InputStream stream, AxiomItemDefinition rootItemDefinition) throws AxiomSyntaxException, FileNotFoundException, IOException { + ModelReactorContext reactorContext =ModelReactorContext.defaultReactor(); + AxiomModelStatementSource statementSource = AxiomModelStatementSource.from(name, stream); + reactorContext.loadModelFromSource(statementSource); + return reactorContext.computeSchemaContext(); + } + + protected static AxiomModelStatementSource source(String name) throws AxiomSyntaxException, IOException { + InputStream stream = new FileInputStream(COMMON_DIR_PATH + name); + return AxiomModelStatementSource.from(name, stream); + } + + protected static AxiomAntlrStatementSource dataSource(String name) throws AxiomSyntaxException, IOException { + InputStream stream = new FileInputStream(COMMON_DIR_PATH + name); + return AxiomAntlrStatementSource.from(name, stream); + } +} diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomExtension.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomExtension.java new file mode 100644 index 00000000000..f9a8290512f --- /dev/null +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomExtension.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.util.Optional; + +import org.testng.annotations.Test; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Type; +import com.evolveum.axiom.lang.impl.ModelReactorContext; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + + +public class TestAxiomExtension extends AbstractReactorTest { + + private static final AxiomName PERSON = AxiomName.from("https://example.org", "Person"); + private static final AxiomName STORAGE = AxiomName.from("https://example.org/extension", "type"); + + private static final AxiomName PERSON_EXTENSION = AxiomName.from("https://schema.org", "SchemaOrgPerson"); + private static final String DIR = "multimodel/extension/"; + private static final String SCHEMA_ORG = DIR + "person-extension.axiom"; + private static final String BASE = DIR + "test-person.axiom"; + private static final String ORDER = DIR + "declaration-order.axiom"; + private static final String LANG_EXT = DIR + "language-extension.axiom"; + private static final String LANG_EXT_USE = DIR + "language-extension-use.axiom"; + + @Test + public void axiomTestExtension() throws IOException, AxiomSyntaxException { + ModelReactorContext context = ModelReactorContext.defaultReactor(); + context.loadModelFromSource(source(SCHEMA_ORG)); + context.loadModelFromSource(source(BASE)); + AxiomSchemaContext schemaContext = context.computeSchemaContext(); + Optional personDef = schemaContext.getType(PERSON); + assertTrue(personDef.isPresent()); + Optional extPersonDef = schemaContext.getType(PERSON_EXTENSION); + assertTrue(extPersonDef.isPresent()); + + for(AxiomName item : extPersonDef.get().itemDefinitions().keySet()) { + assertTrue(personDef.get().itemDefinition(item).isPresent()); + } + } + + @Test + public void axiomTestOrder() throws IOException, AxiomSyntaxException { + ModelReactorContext context = ModelReactorContext.defaultReactor(); + context.loadModelFromSource(source(ORDER)); + AxiomSchemaContext schemaContext = context.computeSchemaContext(); + + AxiomTypeDefinition langExtDef = schemaContext.getType(Type.AUGMENTATION_DEFINITION.name()).get(); + + Optional personDef = schemaContext.getType(PERSON); + assertTrue(personDef.isPresent()); + assertEquals(2, personDef.get().itemDefinitions().entrySet().size()); + } + + @Test + public void axiomTestLanguageExtension() throws IOException, AxiomSyntaxException { + + assertTrue(Type.AUGMENTATION_DEFINITION.isSubtypeOf(Type.TYPE_DEFINITION)); + + ModelReactorContext context = ModelReactorContext.defaultReactor(); + context.loadModelFromSource(source(LANG_EXT)); + AxiomSchemaContext schemaContext = context.computeSchemaContext(); + + AxiomTypeDefinition typeDef = schemaContext.getType(Type.TYPE_DEFINITION.name()).get(); + assertNotNull(typeDef.itemDefinition(STORAGE).get()); + + ModelReactorContext extendedLanguage = ModelReactorContext.reactor(schemaContext); + extendedLanguage.loadModelFromSource(source(LANG_EXT)); + extendedLanguage.loadModelFromSource(source(LANG_EXT_USE)); + schemaContext = extendedLanguage.computeSchemaContext(); + + Optional personDef = schemaContext.getType(PERSON); + assertTrue(personDef.isPresent()); + + AxiomItem extension = personDef.get().asComplex().get().item(STORAGE).get(); + + assertFalse(extension.values().isEmpty(), "Extension statements should be available."); + assertEquals(2, personDef.get().itemDefinitions().entrySet().size()); + } + +} diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomInfra.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomInfra.java new file mode 100644 index 00000000000..8f7f8cad7cf --- /dev/null +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomInfra.java @@ -0,0 +1,107 @@ +package com.evolveum.axiom.lang.test; + +import static org.testng.Assert.assertEquals; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.testng.annotations.Test; + +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.api.stream.AxiomItemTarget; +import com.evolveum.axiom.concepts.SourceLocation; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; +import com.evolveum.axiom.lang.impl.ModelReactorContext; +import com.evolveum.axiom.lang.spi.AxiomSemanticException; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + +public class TestAxiomInfra extends AbstractReactorTest { + + private static final SourceLocation TEST = SourceLocation.from("test", -1, -1); + + private static final String PRISM = "prism/prism.axiom"; + private static final String PRISM_TEST = "prism/prism-infra.axiom"; + + private static final AxiomName PRISM_MODEL = AxiomName.from("http://midpoint.evolveum.com/xml/ns/public/common/prism", "PrismModel"); + + + @Test + void testDataStreamApi() { + AxiomSchemaContext context = ModelReactorContext.defaultReactor().computeSchemaContext(); + AxiomItemTarget simpleItem = new AxiomItemTarget(context); + simpleItem.startItem(Item.MODEL_DEFINITION.name(), TEST); + simpleItem.startValue("test", TEST); + simpleItem.endValue(TEST); + simpleItem.endItem(TEST); + + AxiomItem result = simpleItem.get(); + assertModel(result,"test"); + } + + @Test + void testInfraValueApi() { + AxiomSchemaContext context = ModelReactorContext.defaultReactor().computeSchemaContext(); + AxiomItemTarget simpleItem = new AxiomItemTarget(context); + simpleItem.startItem(Item.MODEL_DEFINITION.name(), TEST); + simpleItem.startValue(null, TEST); + simpleItem.startInfra(AxiomValue.VALUE, TEST); + simpleItem.startValue("test", TEST); + simpleItem.endValue(TEST); + simpleItem.endInfra(TEST); + simpleItem.endValue(TEST); + simpleItem.endItem(TEST); + + AxiomItem result = simpleItem.get(); + assertModel(result,"test"); + } + + @Test + void testInfraTypeApi() throws AxiomSemanticException, AxiomSyntaxException, FileNotFoundException, IOException { + AxiomSchemaContext context = ModelReactorContext.defaultReactor() + .loadModelFromSource(source(PRISM)) + .computeSchemaContext(); + AxiomItemTarget t = new AxiomItemTarget(context); + t.startItem(Item.MODEL_DEFINITION.name(), TEST); // model + t.startValue("test", TEST); // test { + t.startInfra(AxiomValue.TYPE, TEST); // @type + t.startValue(PRISM_MODEL, TEST); // prism:model + t.endValue(TEST); // ; + t.endInfra(TEST); // + t.startItem(PRISM_MODEL.localName("container"), TEST); + t.startValue("test", TEST); + t.endValue(TEST); + t.endItem(TEST); + t.endValue(TEST); + t.endItem(TEST); + + AxiomItem result = t.get(); + AxiomTypeDefinition typeDef = t.get().onlyValue().type().get(); + assertEquals(typeDef.name(), PRISM_MODEL); + assertModel(result,"test"); + } + + @Test + void testInfraSerialized() throws AxiomSemanticException, AxiomSyntaxException, FileNotFoundException, IOException { + AxiomSchemaContext context = ModelReactorContext.defaultReactor() + .loadModelFromSource(source(PRISM)) + .computeSchemaContext(); + AxiomItemTarget t = new AxiomItemTarget(context); + source(PRISM_TEST).stream(t); + + AxiomItem result = t.get(); + AxiomTypeDefinition typeDef = t.get().onlyValue().type().get(); + assertEquals(typeDef.name(), PRISM_MODEL); + assertModel(result,AxiomName.from("http://example.org", "test")); + } + + + private void assertModel(AxiomItem result, Object name) { + AxiomComplexValue model = result.onlyValue().asComplex().get(); + assertEquals(model.item(Item.NAME).get().onlyValue().value(), name); + } +} diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomMultimodule.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomMultimodule.java new file mode 100644 index 00000000000..6421d86036e --- /dev/null +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomMultimodule.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.test; + +import static org.testng.Assert.assertEquals; +import java.io.IOException; +import org.testng.annotations.Test; + +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; +import com.evolveum.axiom.lang.impl.ModelReactorContext; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + +public class TestAxiomMultimodule extends AbstractReactorTest { + + private static final String DIR = "multimodel/ref/"; + private static final String SCHEMA_ORG = DIR + "schema-org-person.axiom"; + private static final String FOAF = DIR + "foaf-person.axiom"; + private static final String TEST = DIR + "test-person.axiom"; + private static final String TEST_INVALID = DIR + "test-person.axiom.invalid"; + + @Test + public void axiomTestImports() throws IOException, AxiomSyntaxException { + ModelReactorContext context = ModelReactorContext.defaultReactor(); + context.loadModelFromSource(source(SCHEMA_ORG)); + context.loadModelFromSource(source(FOAF)); + context.loadModelFromSource(source(TEST)); + AxiomSchemaContext schemaContext = context.computeSchemaContext(); + + AxiomItemDefinition modelDef = schemaContext.getRoot(Item.MODEL_DEFINITION.name()).get(); + assertEquals(modelDef.name(), Item.MODEL_DEFINITION.name()); + + } + +} 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 a3e28068d09..6d9f7be2b17 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 @@ -10,45 +10,26 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.util.Optional; import org.testng.annotations.Test; - -import com.evolveum.axiom.api.AxiomIdentifier; -import com.evolveum.axiom.concepts.Lazy; -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.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Item; import com.evolveum.axiom.lang.api.AxiomBuiltIn.Type; - -import com.evolveum.axiom.lang.impl.AxiomStatementSource; - -import com.evolveum.axiom.lang.impl.AxiomSyntaxException; import com.evolveum.axiom.lang.impl.ModelReactorContext; -import com.evolveum.midpoint.tools.testng.AbstractUnitTest; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; -public class TestAxiomParser extends AbstractUnitTest { +public class TestAxiomParser extends AbstractReactorTest { - private static final String COMMON_DIR_PATH = "src/test/resources/"; private static final String BASE_EXAMPLE = "base-example.axiom"; private static final String COMMON_CORE = "common-core.axiom"; private static final String SCRIPTING = "scripting.axiom"; - private static final String AXIOM_BUILTIN_TYPES = "axiom-base-types.axiom"; - private static final Lazy AXIOM_TYPES_SCHEMA = Lazy.from(() -> { - try { - InputStream stream = new FileInputStream(COMMON_DIR_PATH + AXIOM_BUILTIN_TYPES); - return AxiomStatementSource.from(AXIOM_BUILTIN_TYPES, stream); - } catch (Exception e) { - throw new IllegalStateException(e); - } - }); + + @Test public void axiomSelfDescribingTest() throws IOException, AxiomSyntaxException { @@ -62,9 +43,9 @@ public void axiomSelfDescribingTest() throws IOException, AxiomSyntaxException { // Default reactor has Axiom model already loaded ModelReactorContext folowupContext = ModelReactorContext.reactor(modelContext); //folowupContext.loadModelFromSource(statementSource); - AxiomSchemaContext selfparsedContext = bootstrapContext.computeSchemaContext(); + AxiomSchemaContext selfparsedContext = folowupContext.computeSchemaContext(); assertNotNull(selfparsedContext.getRoot(Item.MODEL_DEFINITION.name())); - assertTrue(selfparsedContext.getType(Type.IDENTIFIER_DEFINITION.name()).get().item(Item.ID_MEMBER.name()).get().required()); + assertTrue(selfparsedContext.getType(Type.IDENTIFIER_DEFINITION.name()).get().itemDefinition(Item.ID_MEMBER.name()).get().required()); } @@ -74,42 +55,8 @@ private void assertTypedefBasetype(Optional optional) { assertEquals(typeDef.superType().get().name(), Type.BASE_DEFINITION.name()); } - private void assertInstanceOf(Class clz, Object value) { assertTrue(clz.isInstance(value)); } - @Test - public void moduleHeaderTest() throws IOException, AxiomSyntaxException { - AxiomSchemaContext context = parseFile(BASE_EXAMPLE); - assertNotNull(context.getType(AxiomIdentifier.axiom("Example")).get()); - } - - @Test - public void commonCoreTest() throws IOException, AxiomSyntaxException { - AxiomSchemaContext context = parseFile(COMMON_CORE); - } - - @Test - public void scriptingTest() throws IOException, AxiomSyntaxException { - AxiomSchemaContext context = parseFile(SCRIPTING); - } - - private AxiomSchemaContext parseFile(String name) throws AxiomSyntaxException, FileNotFoundException, IOException { - return parseInputStream(name, new FileInputStream(COMMON_DIR_PATH + name)); - } - - private AxiomSchemaContext parseInputStream(String name, InputStream stream) throws AxiomSyntaxException, FileNotFoundException, IOException { - return parseInputStream(name, stream, AxiomBuiltIn.Item.MODEL_DEFINITION); - } - - 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); - reactorContext.loadModelFromSource(AXIOM_TYPES_SCHEMA.get()); - return reactorContext.computeSchemaContext(); - } - - } diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomPrism.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomPrism.java new file mode 100644 index 00000000000..a60a32c94d9 --- /dev/null +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestAxiomPrism.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.test; + +import static org.testng.Assert.assertNotNull; + +import java.io.IOException; +import org.testng.annotations.Test; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.lang.impl.ModelReactorContext; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + +public class TestAxiomPrism extends AbstractReactorTest { + + private static final AxiomName PERSON = AxiomName.from("https://example.org", "Person"); + private static final AxiomName STORAGE = AxiomName.from("https://example.org/extension", "type"); + + private static final AxiomName PERSON_EXTENSION = AxiomName.from("https://schema.org", "SchemaOrgPerson"); + private static final String DIR = "prism/"; + private static final String PRISM = DIR + "prism.axiom"; + private static final String COMMON_CORE = DIR + "common-core.axiom"; + private static final String COMMON_CORE_PRISM = DIR + "common-core.prism"; + + private ModelReactorContext prismReactor() throws AxiomSyntaxException, IOException { + ModelReactorContext context = ModelReactorContext.defaultReactor(); + context.loadModelFromSource(source(PRISM)); + AxiomSchemaContext schemaContext = context.computeSchemaContext(); + + ModelReactorContext extendedLanguage = ModelReactorContext.reactor(schemaContext); + extendedLanguage.loadModelFromSource(source(PRISM)); + return extendedLanguage; + } + + @Test + public void axiomTestPrismInAxiom() throws IOException, AxiomSyntaxException { + + ModelReactorContext extendedLanguage = prismReactor(); + extendedLanguage.loadModelFromSource(source(COMMON_CORE)); + AxiomSchemaContext schemaContext = extendedLanguage.computeSchemaContext(); + + assertNotNull(schemaContext); + } + + @Test + public void axiomTestPrismInPrism() throws IOException, AxiomSyntaxException { + + ModelReactorContext extendedLanguage = prismReactor(); + extendedLanguage.loadModelFromSource(source(COMMON_CORE_PRISM)); + AxiomSchemaContext schemaContext = extendedLanguage.computeSchemaContext(); + + assertNotNull(schemaContext); + } + +} diff --git a/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestTypeDerivation.java b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestTypeDerivation.java new file mode 100644 index 00000000000..3f6661196c5 --- /dev/null +++ b/infra/axiom/src/test/java/com/evolveum/axiom/lang/test/TestTypeDerivation.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.axiom.lang.test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Map.Entry; +import java.util.Optional; + +import org.testng.annotations.Test; + +import com.evolveum.axiom.api.AxiomName; +import com.evolveum.axiom.api.AxiomComplexValue; +import com.evolveum.axiom.api.AxiomItem; +import com.evolveum.axiom.api.AxiomValue; +import com.evolveum.axiom.api.meta.Inheritance; +import com.evolveum.axiom.api.schema.AxiomItemDefinition; +import com.evolveum.axiom.api.schema.AxiomSchemaContext; +import com.evolveum.axiom.api.schema.AxiomTypeDefinition; +import com.evolveum.axiom.api.stream.AxiomItemTarget; +import com.evolveum.axiom.lang.antlr.AxiomAntlrStatementSource; +import com.evolveum.axiom.lang.impl.ModelReactorContext; +import com.evolveum.axiom.lang.spi.AxiomNameResolver; +import com.evolveum.axiom.lang.spi.AxiomSyntaxException; + +public class TestTypeDerivation extends AbstractReactorTest { + + private static final AxiomName DERIVED_PERSON = AxiomName.from("https://example.org/derived", "Person"); + private static final AxiomName FIRST_NAME = DERIVED_PERSON.localName("firstName"); + private static final AxiomName LAST_NAME = DERIVED_PERSON.localName("lastName"); + private static final AxiomName NAME = AxiomName.from("https://example.org/base", "name"); + private static final String DIR = "multimodel/derived/"; + private static final String BASE = DIR + "base-person.axiom"; + private static final String DERIVED = DIR + "derived-person.axiom"; + private static final String JOHN_DOE_FILE = DIR + "john-doe.axiomd"; + + + private AxiomSchemaContext loadModel() throws AxiomSyntaxException, IOException { + ModelReactorContext context = ModelReactorContext.defaultReactor(); + context.loadModelFromSource(source(BASE)); + context.loadModelFromSource(source(DERIVED)); + return context.computeSchemaContext(); + } + + @Test + public void axiomTestInheritance() throws IOException, AxiomSyntaxException { + AxiomSchemaContext schemaContext = loadModel(); + + Optional personDef = schemaContext.getType(DERIVED_PERSON); + assertTrue(personDef.isPresent()); + for (Entry idDef : personDef.get().itemDefinitions().entrySet()) { + AxiomItemDefinition item = idDef.getValue(); + assertEquals(idDef.getKey(), Inheritance.adapt(DERIVED_PERSON, item), " should have different namespace"); + } + } + + @Test + public void axiomData() throws AxiomSyntaxException, FileNotFoundException, IOException { + AxiomSchemaContext context = loadModel(); + AxiomAntlrStatementSource stream = dataSource(JOHN_DOE_FILE); + AxiomItemTarget target = new AxiomItemTarget(context, AxiomNameResolver.defaultNamespace(DERIVED_PERSON.namespace())); + stream.stream(target); + AxiomItem root = target.get(); + assertEquals(root.name(), DERIVED_PERSON.localName("person")); + AxiomComplexValue person = root.onlyValue().asComplex().get(); + assertEquals(person.item(NAME).get().onlyValue().value(), "John Doe"); + assertEquals(person.item(FIRST_NAME).get().onlyValue().value(), "John"); + + + } +} diff --git a/infra/axiom/src/test/resources/axiom-data.axiom b/infra/axiom/src/test/resources/axiom-data.axiom new file mode 100644 index 00000000000..d60cd0edbbd --- /dev/null +++ b/infra/axiom/src/test/resources/axiom-data.axiom @@ -0,0 +1,51 @@ +model axiom-data { + + import axiom; + + type AxiomValue { + metadata type { + type axiom:AxiomTypeReference; + } + item metadata; + } + + type AxiomSimpleValue { + supertype AxiomValue; + argument value; + item value; + } + + type AxiomComplexValue { + supertype AxiomValue; + item item { + type AxiomItem; + } + } + + type AxiomItem { + metadata definition { + type AxiomItemDefinition; + operational true; + } + item value { + type AxiomValue; + } + } + + axiom ComplexValueHasDefinedItems { + // We have language Axioms + // Complex Value contains only items defined in its type. + target AxiomComplexValue; + all item/@definition { + subsetOf @type/item; + } + } + + axiom ItemAllowsOnlyValuesOfSType { + // Item values must be of type. + target AxiomItem; + each value/@type/definition { + subsetOf @definition/type; // + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/base-example.axiom b/infra/axiom/src/test/resources/base-example.axiom index 3906c3f40d5..805d98cabbe 100644 --- a/infra/axiom/src/test/resources/base-example.axiom +++ b/infra/axiom/src/test/resources/base-example.axiom @@ -50,11 +50,6 @@ model model-header { } } - object { - name User2; - itemName user; - } - // Alternative representation of a prism object definition // (doesn't interfere with item { type ... } in structured types) object { diff --git a/infra/axiom/src/test/resources/multimodel/derived/base-person.axiom b/infra/axiom/src/test/resources/multimodel/derived/base-person.axiom new file mode 100644 index 00000000000..a421db08a3b --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/derived/base-person.axiom @@ -0,0 +1,11 @@ +model base-person { + + namespace "https://example.org/base"; + + type Person { + argument name; + item name { + type string; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/derived/derived-person.axiom b/infra/axiom/src/test/resources/multimodel/derived/derived-person.axiom new file mode 100644 index 00000000000..c20b688ec54 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/derived/derived-person.axiom @@ -0,0 +1,23 @@ +model derived-person { + + namespace "https://example.org/derived"; + + import "https://example.org/base" { + prefix base; + } + + root person { + type Person; + } + + type Person { + supertype base:Person; + + item firstName { + type string; + } + item lastName { + type string; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/derived/john-doe.axiomd b/infra/axiom/src/test/resources/multimodel/derived/john-doe.axiomd new file mode 100644 index 00000000000..532784466de --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/derived/john-doe.axiomd @@ -0,0 +1,4 @@ +person "John Doe" { + firstName "John"; + lastName "Doe"; +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/extension/declaration-order.axiom b/infra/axiom/src/test/resources/multimodel/extension/declaration-order.axiom new file mode 100644 index 00000000000..048c8211a7f --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/extension/declaration-order.axiom @@ -0,0 +1,17 @@ +model test-person { + + namespace "https://example.org"; + + type Person { + item name { + type string; + } + } + + augmentation PersonExtension { + target Person; + item fullName { + type string; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/extension/language-extension-use.axiom b/infra/axiom/src/test/resources/multimodel/extension/language-extension-use.axiom new file mode 100644 index 00000000000..9f40e921369 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/extension/language-extension-use.axiom @@ -0,0 +1,25 @@ +model language-extension-use { + + namespace "https://example.org"; + + import "https://example.org/extension" { + prefix storage; + } + + type Address { + item street { + type string; + } + storage:type embedded; + } + + type Person { + item name { + type string; + } + item address { + type Address; + } + storage:type table; + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/extension/language-extension.axiom b/infra/axiom/src/test/resources/multimodel/extension/language-extension.axiom new file mode 100644 index 00000000000..d18c15d1ea2 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/extension/language-extension.axiom @@ -0,0 +1,16 @@ +model language-extension { + + namespace "https://example.org/extension"; + + import "https://schema.evolveum.com/ns/axiom/model" { + prefix axiom; + } + + augmentation Storage { + target axiom:AxiomTypeDefinition; + + item type { + type string; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/extension/person-extension.axiom b/infra/axiom/src/test/resources/multimodel/extension/person-extension.axiom new file mode 100644 index 00000000000..c0881c6de47 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/extension/person-extension.axiom @@ -0,0 +1,19 @@ +model schema-org-person { + + namespace "https://schema.org"; + + import "https://example.org" { + prefix base; + } + + augmentation SchemaOrgPerson { + target base:Person; + + item honorificPrefix { + type string; + } + item honorificSuffix { + type string; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/extension/test-person.axiom b/infra/axiom/src/test/resources/multimodel/extension/test-person.axiom new file mode 100644 index 00000000000..4e6c8e0baa3 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/extension/test-person.axiom @@ -0,0 +1,10 @@ +model test-person { + + namespace "https://example.org"; + + type Person { + item name { + type string; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/ref/foaf-person.axiom b/infra/axiom/src/test/resources/multimodel/ref/foaf-person.axiom new file mode 100644 index 00000000000..1c7e3330c8c --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/ref/foaf-person.axiom @@ -0,0 +1,17 @@ +model foaf-person { + + namespace "http://xmlns.com/foaf/0.1/"; + + type Person { + item givenName { + type string; + } + item familyName { + type string; + } + item additionalName { + type string; + } + } + +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/ref/schema-org-person.axiom b/infra/axiom/src/test/resources/multimodel/ref/schema-org-person.axiom new file mode 100644 index 00000000000..270c138c8f5 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/ref/schema-org-person.axiom @@ -0,0 +1,19 @@ +model foaf-person { + + namespace "https://schema.org"; + //prefix schemaorg; + + + type Person { + item givenName { + type string; + } + item familyName { + type string; + } + item additionalName { + type string; + } + } + +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/ref/test-person.axiom b/infra/axiom/src/test/resources/multimodel/ref/test-person.axiom new file mode 100644 index 00000000000..b0bbeef0b53 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/ref/test-person.axiom @@ -0,0 +1,23 @@ +model schema-org-person { + + namespace "https://example.org"; + + import "http://xmlns.com/foaf/0.1/" { + prefix foaf; + } + import "https://schema.org" { + prefix schemaOrg; + } + + type Person { + item name { + type string; + } + item foaf { + type foaf:Person; + } + item schemaorg { + type schemaOrg:Person; + } + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/multimodel/ref/test-person.axiom.invalid b/infra/axiom/src/test/resources/multimodel/ref/test-person.axiom.invalid new file mode 100644 index 00000000000..48d580edea9 --- /dev/null +++ b/infra/axiom/src/test/resources/multimodel/ref/test-person.axiom.invalid @@ -0,0 +1,23 @@ +model invalid-import { + + import foafInvalid { + namespace "foaf"; + } + + import schemaOrg { + namespace "https://schema.org"; + } + + type InvalidGlobal { + item person { + type Person; + } + } + + type InvalidPrefix { + item person { + type InvalidPrefix; + } + } + +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/common-core.axiom b/infra/axiom/src/test/resources/prism/common-core.axiom similarity index 94% rename from infra/axiom/src/test/resources/common-core.axiom rename to infra/axiom/src/test/resources/prism/common-core.axiom index a188f4aa56b..33b029b785f 100644 --- a/infra/axiom/src/test/resources/common-core.axiom +++ b/infra/axiom/src/test/resources/prism/common-core.axiom @@ -11,9 +11,13 @@ model common-core { namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; version "3.0.0"; + import "http://midpoint.evolveum.com/xml/ns/public/common/prism" { + prefix prism; + } + type Object { - object; - itemName object; + prism:object; + prism:itemName object; documentation """ Common supertype for all identity objects. Defines basic properties that each object must have to live in our system (identifier, name). @@ -230,8 +234,8 @@ model common-core { } type AssignmentHolder { - extends Object; - itemName assignmentHolder; + supertype Object; + prism:itemName assignmentHolder; documentation """ Abstract supertype for all object types that can have assignments. """; @@ -356,8 +360,8 @@ model common-core { } type Focus { - extends AssignmentHolder; - itemName focus; + supertype AssignmentHolder; + prism:itemName focus; documentation """ Abstract supertype for all object types that can be focus of full midPoint computation. This basically means objects that have projections. But focal objects also have @@ -536,7 +540,7 @@ model common-core { } type User { - extends Focus; + supertype Focus; documentation """ User object represents a physical user of the system. It differs from the account, as "account" represents a data structure in a target system while @@ -768,7 +772,7 @@ model common-core { } type ObjectReference { - objectReference; + prism:objectReference; documentation """ Reference to an object. It contains OID of the object that it refers to. @@ -836,49 +840,53 @@ model common-core { // as they are also defined in PrismReferenceValue. } - item description { - type string; - documentation """ - Free-form textual description of the object. It is supposed to describe - the object or a construct that it is attached to. + mixin Description { + item description { + type string; + documentation """ + Free-form textual description of the object. It is supposed to describe + the object or a construct that it is attached to. - This information may be presented to midPoint users, even to ordinary end users. - For example role description may be presented to users when they are selecting - roles to request. Therefore the description should be written in a language that - the users can understand. + This information may be presented to midPoint users, even to ordinary end users. + For example role description may be presented to users when they are selecting + roles to request. Therefore the description should be written in a language that + the users can understand. - Description is assumed to be a plan, non-formatted text. - Amount of white space is considered insignificant. E.g. leading and trailing - white space may be skipped, multiple spaces can be collapsed to one and so on. - """; - // ObjectType.description - // 10 + Description is assumed to be a plan, non-formatted text. + Amount of white space is considered insignificant. E.g. leading and trailing + white space may be skipped, multiple spaces can be collapsed to one and so on. + """; + // ObjectType.description + // 10 + } } - item documentation { - type string; - documentation """ - Technical documentation for a particular object or construct. - - The purpose of this element is to document system configuration and behavior. - The documentation will not be presented to end users. In fact, it will probably - not be presented at all in midPoint user interface. This documentation element - is supposed to be a part of the technical documentation of midPoint deployment. - The tools than generate deployment configuration will look for these elements - and combine them to compiled documentation document. - - AsciiDoc formatting is assumed for this element. Any leading or trailing - whitespace is skipped. Indentation equivalent to he indentation of the first - non-blank line of text is also skipped. - """; - // ObjectType.documentation - // 11 + mixin Documentation { + item documentation { + type string; + documentation """ + Technical documentation for a particular object or construct. + + The purpose of this element is to document system configuration and behavior. + The documentation will not be presented to end users. In fact, it will probably + not be presented at all in midPoint user interface. This documentation element + is supposed to be a part of the technical documentation of midPoint deployment. + The tools than generate deployment configuration will look for these elements + and combine them to compiled documentation document. + + AsciiDoc formatting is assumed for this element. Any leading or trailing + whitespace is skipped. Indentation equivalent to he indentation of the first + non-blank line of text is also skipped. + """; + // ObjectType.documentation + // 11 + } } // Example of short version of container definition. - container { + prism:container { name Assignment; - itemName assignment; + prism:itemName assignment; // item description; // TODO make this work // item documentation; // TODO make this work @@ -887,9 +895,9 @@ model common-core { // ... } - container { + prism:container { name Extension; - itemName extension; + prism:itemName extension; documentation """ Extension container that provides generic extensibility mechanism. Almost any extension property can be placed in this container. @@ -902,9 +910,9 @@ model common-core { // 1000 } - object GenericObject { - extends Focus; - itemName genericObject; + prism:object GenericObject { + supertype Focus; + prism:itemName genericObject; documentation """ Generic object for storing unknown (unexpected) object types. @@ -920,8 +928,8 @@ model common-core { } } - container Trigger { - itemName trigger; + prism:container Trigger { + prism:itemName trigger; documentation """ Defines triggers for an object. Trigger is an action that should take place at specified time or under some other condition. @@ -956,8 +964,8 @@ model common-core { } } - container Metadata { - itemName metadata; + prism:container Metadata { + prism:itemName metadata; documentation """ Meta-data about data creation, modification, etc. It may apply to objects but also parts of the object (e.g. assignments). @@ -1192,7 +1200,7 @@ model common-core { type ReferentialIntegrity; // ??? - type Extension; + // type Extension; // common-3 type OperationResult; @@ -1205,7 +1213,8 @@ model common-core { type Activation; // should exist because of "container XXX" - type Trigger; - type Assignment; + // Already defined + // type Trigger; + // type Assignment; } diff --git a/infra/axiom/src/test/resources/prism/common-core.prism b/infra/axiom/src/test/resources/prism/common-core.prism new file mode 100644 index 00000000000..45a3c9a15d6 --- /dev/null +++ b/infra/axiom/src/test/resources/prism/common-core.prism @@ -0,0 +1,1220 @@ +// Copyright (c) 2020 Evolveum and contributors +// +// This work is dual-licensed under the Apache License 2.0 +// and European Union Public License. See LICENSE file for details. + +// This is "common-core.axiom" file containing core definitions from the common-3 namespace. +// Due to its size the common-3 schema is distributed across multiple files. + +prism:model common-core { + + namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; + version "3.0.0"; + + import "http://midpoint.evolveum.com/xml/ns/public/common/prism" { + prefix prism; + } + + type Object { + object; + itemName object; + documentation """ + Common supertype for all identity objects. Defines basic properties + that each object must have to live in our system (identifier, name). + + All objects are identified by OID. The OID is an immutable identifier + (usually UUID). Except the OID all the objects have human-readable name. + The name is usually unique for each object type, but this is not a + strict requirement. + + Note: object type is fixed, it cannot be changed. The object retains its + type from the time it was created to the end of its life. + """; + item name { + type PolyString; + documentation """ + Human-readable, mutable name of the object. It + may also be an identifier (login name, group name). + It is usually unique in the respective context of + interpretation. E.g. the name of the UserType subtype + is usually unique in the whole system. + The name of the ShadowType subtype is usually unique in the + scope of resource (target system) that it belongs to. + + The name may not be human-readable in a sense to display + to a common end-user. It is intended to be displayed to + IDM system administrator. Therefore it may contain quite + a "ugly" structures such as LDAP DN or URL. + + Name is mutable. It is considered to be ordinary property + of the object. Therefore it can be changed by invoking + usual modifyObject operations. However, change of the name + may have side effects (rename process). + + Although name is specified as optional by this schema, it + is in fact mandatory for most object types. The reason for + specifying the name as optional is that the name may be + generated by the system instead of supplied by the clients. + However, all objects stored in the repository must have a name. + """; + // ObjectType.name + // 0 + // true + } + + // Note: the description property is referenced from various contexts (e.g. Object and Assignment). + // To avoid duplication we use global property definition. + // item description; // TODO make this work + + // The same is true for documentation. + // item documentation; // TODO make this work + + item subtype { + type string; + maxOccurs unbounded; + documentation """ + Type of the object. It is used to distinguish what a specific object + represents. Whether it is a different kind of organizational unit, project, + team, or different kind of user, etc. + """; + // ObjectType.subtype + // 15 + } + + item fetchResult { + type OperationResult; + documentation """ + Result of the operation that fetched this instance of the object. + It is mostly used to indicate that the object is not complete or + there is some problem with the object. This is used instead of + exception if the object is part of larger structures (lists as in + list/search operations or composite objects). If not present then + the "SUCCESS" state is assumed. + + This field is TRANSIENT. It must only be used in runtime. It should + never be stored in the repository. + """; + // a:operational + } + + // item extension; // TODO make this work + + item trigger { + type Trigger; + maxOccurs unbounded; + documentation """ + Triggers for this object. They drive invocations of corresponding trigger handlers + at specified time. + """; + // a:operational + } + + item parentOrg { // TODO or should we use 'Ref' suffix i.e. 'parentOrgRef'? + type ObjectReference; + maxOccurs unbounded; + documentation """ + Set of the orgs (organizational units, projects, teams) that the object relates to. + This usually means that the object belongs to them but it may have other meanings as well + (e.g. user manages an organizational unit). + """; + // tns:OrgType + // OrgType.parentOrganization + // 240 + // + } + + item tenant { + type ObjectReference; + documentation """ + Reference to the tenant to which this object belongs. It is a computed value set automatically + by midPoint. It is determined from the organizational structure. Even though this value is + computed it is also stored in the repository due to performance reasons. + """; + // tns:OrgType + // OrgType.tenant + // 250 + // true + } + + item lifecycleState { + type string; + documentation """ + Lifecycle state of the object. This property defines whether the + object represents a draft, proposed definition, whether it is active, + deprecated, and so on. + + There are few pre-defined lifecycle states. But custom lifecycle states + may also be defined. Pre-defined lifecycle states are: + + - draft: Definition of the new object in progress. The object is + NOT active. The definition may change at any moment. It is + not ready yet. + - proposed: Definition of a new object is ready for use, but there + is still a review process to be applied (e.g. approval). + The object is NOT active. However the definition should + not change in this state. + - active: Active and working definition. Ready to be used without + any unusual limitations. + - deprecated: Active definition which is being phased out. The + definition is still fully operational. But it should not + be used for new assignments. E.g. it should not be requested, + it should not be approved, etc. + - archived: Inactive historical definition. It is no longer used. + It is maintained only for historical, auditing and + sentimental reasons. + - failed: Unexpected error has occurred during object lifecycle. Result + of that event is that the object is rendered inactive. + The situation cannot be automatically remedied. Manual action + is needed. + """; + // ObjectType.lifecycleState + // 20 + since "3.5"; + // + } + + item operationExecution { + type OperationExecution; + maxOccurs unbounded; + documentation """ + Description of recent operations executed on this object (or related objects, e.g. shadows + in case of a focal object). The number of operations to be kept here is configurable. + """; + since "3.6"; + // true + } + + item lensContext { + type LensContext; + documentation """ + Model context describing executed operation. + """; + since "4.0"; + // true + } + + item policySituation { + type uri; + maxOccurs unbounded; + documentation """ + The policy situation(s) of this object. The situations are result of + evaluation of the policy rules. This property is recorded for each object + and can be used for reporting, diagnostics, target selection in certification + campaigns, etc. + """; + since "3.5"; + // true + } + + item policyException { + type PolicyException; + maxOccurs unbounded; + documentation """ + Recorded exception from a policy rule. The exceptions that are approved are + recoded here to avoid re-evaluating and re-approving them all the time. + This is EXPERIMENTAL functionality. It is likely to change in the near future. + """; + since "3.5"; + // true + } + + item diagnosticInformation { + type DiagnosticInformation; + maxOccurs unbounded; + documentation """ + Diagnostic information attached to this object. + """; + since "4.0"; + // true + } + + // Note that oid and version are not defined here. These are intrinsic parts of prism objects + // so they do not have to be mentioned in the schema. + // TODO: is this OK? See also related questions in ObjectReference type. + } + + type AssignmentHolder { + supertype Object; + itemName assignmentHolder; + documentation """ + Abstract supertype for all object types that can have assignments. + """; + + item assignment { + type Assignment; + maxOccurs unbounded; + documentation """ + Set of object's assignments. + Assignments define the privileges and "features" that this object should have, that + this object is entitled to. Typical assignment will point to a role or define + a construction of an account. + + Assignments represent what the object SHOULD HAVE. The assignments represent a policy, + a desired state of things (cf. linkRef). + """; + // FocusType.assignmentKey + } + + item iteration { + type int; // TODO + documentation """ + Iteration number. Starts with 0. It is used to iteratively find unique identifier + for the object. + """; + // true + } + + item iterationToken { + type string; + documentation """ + Iteration token. String value that is usually a suffix to the identifier based + on iteration number. E.g. ".007". It is used to iteratively find unique identifier + for the object. + """; + // true + } + + item archetype { + type ObjectReference; + maxOccurs unbounded; + documentation """ + References to all applicable archetypes, including "indirect" archetypes such as archetype supertypes. + Contains references to active archetypes only. + + Note: the value of this reference is only updated when object is recomputed. + Therefore if a role definition changes then all the affected objects must be recomputed + for this reference to be consistent. + + This is an operational property. It is set and managed by the system. It is used + for efficient use of archetypes. + """; + // tns:ArchetypeType + // true + // AssignmentHolderType.archetypeRef + since "4.0"; + } + + item roleMembership { + type ObjectReference; + maxOccurs unbounded; + documentation """ + References to abstract roles (roles, orgs, services) that this focus currently belongs to - directly + or indirectly. This reference points to all the roles in the role hierarchy. It only points to + the roles that were evaluated as active during last recompute (conditions were true, validity + constraints not violated). + + Note: the value of this reference is only updated when a focal object is recomputed. + Therefore if a role definition changes then all the affected focal objects must be recomputed + for this reference to be consistent. + + Roles mentioned here are those that are NOT obtained via delegation, i.e. "deputy" relations. + Relations acquired by delegation are listed in delegatedRef item. + + This is an operational property. It is set and managed by the system. It is used + for efficient search of all current role members, e.g. for the purpose of displaying this + information in the GUI. + """; + // tns:AbstractRoleType + // true + // FocusType.roleMembershipRef + } + + item delegated { + type ObjectReference; + maxOccurs unbounded; + documentation """ + References to objects (abstract roles as well as users) obtained via delegation. + If A1 is a deputy of A, its delegatedRef contains a union of A, A.roleMembershipRef and + A.delegatedRef. + + This is an operational property. It is set and managed by the system. It is used + for efficient search of all current role members, e.g. for the purpose of displaying this + information in the GUI. + """; + // tns:FocusType + // true + since "3.5"; + } + + item roleInfluence { + type ObjectReference; + maxOccurs unbounded; + documentation """ + References to abstract roles (roles and orgs) that this focus may directly belong to. + This reference only points to the next role in the hierarchy. However, it is backed by + a "closure" index in the repository subsystem. Therefore it can efficiently support tree-like + queries. This reference points to the roles for whose the condition is not true. + Therefore it does not reliably show + who actually has a role. It shows potential role members - all the object that are possibly + influenced when a role definition changes. + + This is an operational property. It is set and managed by the system. It is used + for efficient search of all possible role members, e.g. for the purpose of recomputing + all role members after the role definition is changed. + + TODO. NOT IMPLEMENTED YET. EXPERIMENTAL. UNSTABLE. + """; + // tns:AbstractRoleType + // true + } + } + + type Focus { + supertype AssignmentHolder; + itemName focus; + documentation """ + Abstract supertype for all object types that can be focus of full midPoint computation. + This basically means objects that have projections. But focal objects also have + activation, they may have personas, etc. + """; + item link { + type ObjectReference; + maxOccurs unbounded; + documentation """ + Set of shadows (projections) linked to this focal object. + E.g. a set of accounts linked to a user. This is the set of + shadows that belongs to the focal object in a sense + that these shadows represents the focal object on the resource. + E.g. The set of accounts that represent the same midPoint user (the + same physical person, they are "analogous"). + + Links define what the object HAS. The links reflect real state of things + (cf. assignment). + """; + // tns:ShadowType + } + + item persona { + type ObjectReference; + maxOccurs unbounded; + documentation """ + Set of personas linked to this focal object. + E.g. a set of virtual identities linked to a user. This is the set of + "secondary" focal objects that belongs to this focal object in a sense + that the current focal object is in control over the linked focal objects. + E.g. this reference can be used to link user object which specified a physical + person with his virtual identities (personas) that specify his identity as an + employee, system administrator, customer, etc. + The default meaning is that the personas are "analogous", i.e. the represent + different facets of the same physical person. However, this meaning may be + theoretically overridden by using various relation parameters in this reference. + + This reference define what the object HAS. The links reflect real state of + things (cf. assignment). + """; + // tns:FocusType + since "3.6"; + } + + item activation { + type Activation; + } + + item jpegPhoto { + type binary; + documentation """ + Photo corresponding to the user / org / role / service. + """; + // FocusType.jpegPhoto + } + + item costCenter { + type string; + documentation """ + The name, identifier or code of the cost center to which the object belongs. + + Please note that organization objects (OrgType) also have a costCenter property. + Therefore it is usual that if a user belongs to an organization the costCenter from + the organization is used. Therefore this property is usually used only for users that + do not belong to any organization or for users that have different cost center than + the one defined by the organization. + """; + // FocusType.costCenter + // 420 + } + + item locality { + type PolyString; + documentation """ + Primary locality of the object, the place where + the (e.g.) user usually works, the country, city or + building that he belongs to. The specific meaning + and form of this property is deployment-specific. + """; + // FocusType.locality + // 450 + } + + item preferredLanguage { + type string; + documentation """ + Indicates user's preferred language, usually for the purpose of localizing + user interfaces. The format is IETF language tag defined in BCP 47, where + underscore is used as a subtag separator. This is usually a ISO 639-1 two-letter + language code optionally followed by ISO 3166-1 two letter country code + separated by underscore. The languages that do not have country-specific + variants are usually specified by using a two-letter country code ("sk", + "cs", "tr"). Languages with country-specific variants have country-specific + subtags ("pt_BR", "zn_CN"). + If no value is specified in this property then system default locale is assumed. + + Examples: + - en_US + - sk + - cs + - pt_BR + """; + // FocusType.preferredLanguage + // 200 + // + } + + item locale { + type string; + documentation """ + Defines user's preference in displaying currency, dates and other items + related to location and culture. The format is IETF language tag defined in BCP 47, where + underscore is used as a subtag separator. This is usually a ISO 639-1 two-letter + language code optionally followed by ISO 3166-1 two letter country code + separated by underscore. The languages that do not have country-specific + variants are usually specified by using a two-letter country code ("sk", + "cs", "tr"). Languages with country-specific variants have country-specific + subtags ("pt_BR", "zn_CN"). + If not specified then system default locale is assumed. + + Examples: + - en_US + - sk + - cs + - pt_BR + """; + // FocusType.locale + // 210 + // + } + + item timezone { + type string; + documentation """ + User's preferred timezone. It is specified in the "tz database" (a.k.a "Olson") + format. If not specified then system default timezone is assumed. + + Examples: + - Europe/Bratislava + """; + // FocusType.timezone + // 220 + // + } + + item emailAddress { + type string; + documentation """ + E-Mail address of the user, org. unit, etc. This is the address + supposed to be used for communication with the + user, org. unit, etc. E.g. IDM system may send notifications + to the e-mail address. It is NOT supposed to be + full-featured e-mail address data structure + e.g. for the purpose of complex address-book application. + """; + // FocusType.emailAddress + // 300 + } + + item telephoneNumber { + type string; + documentation """ + Primary telephone number of the user, org. unit, etc. + """; + // FocusType.telephoneNumber + // 310 + } + + item credentials { + type Credentials; + documentation """ + The set of focus's credentials (such as passwords). + """; + // FocusType.credentials + } + } + + type User { + supertype Focus; + documentation """ + User object represents a physical user of the system. + It differs from the account, as "account" represents a data structure in a target system while + "user" represents data structure in midPoint. One user typically has many accounts. + Properties of User object typically describe the user as a physical person. + Therefore the user object defines handful of properties that are commonly used to describe users + in the IDM solutions (employees, customers, partners, etc.) Custom extensions are possible by utilizing + the "extension" container. + """; + + item fullName { + type PolyString; + documentation """ + Full name of the user with all the decorations, + middle name initials, honorific title and any + other structure that is usual in the cultural + environment that the system operates in. This + element is intended to be displayed to + a common user of the system. + + Examples: + - cpt. Jack Sparrow + - William "Bootstrap" Turner + - James W. Random, PhD. + - Vladimir Iljic Lenin + - Josip Broz Tito + - Chuck Norris + """; + // UserType.fullName + // 100 + // true + } + + item givenName { + type PolyString; + documentation """ + Given name of the user. It is usually the first + name of the user, but the order of names may + differ in various cultural environments. This + element will always contain the name that was + given to the user at birth or was chosen + by the user. + + Examples: + - Jack + - Chuck + """; + // UserType.givenName + // 110 + // true + } + + item familyName { + type PolyString; + documentation """ + Family name of the user. It is usually the last + name of the user, but the order of names may + differ in various cultural environments. This + element will always contain the name that was + inherited from the family or was assigned + to a user by some other means. + + Examples: + - Sparrow + - Norris + """; + // UserType.familyName + // 120 + // true + } + + item additionalName { + type PolyString; + documentation """ + Middle name, patronymic, matronymic or any other name of a person. It is usually the + middle component of the name, however that may be culture-dependent. + + Examples: + - Walker + - John + - Iljic + """; + // UserType.additionalName + // 130 + } + + item nickName { + type PolyString; + documentation """ + Familiar or otherwise informal way to address a person. + + Examples: + - Bootstrap + - Bobby< + + The meaning of this property is to take part in the formatted full + name of the person, e.g. William "Bootstrap" Turner. It is not intended + to be used as a username or login name. This value is usually changeable + by the user itself and it defines how the user wants other to address him. + Therefore it is not ideal for use as an identifier. + """; + // UserType.nickname + // 140 + } + + item honorificPrefix { + type PolyString; + documentation """ + Honorific titles that go before the name. + Examples: + - cpt. + - Ing. + - Sir + + This property is single-valued. If more + than one title is applicable, they have to be represented in + a single string (concatenated) form in the correct order. + """; + // UserType.honorificPrefix + // 150 + } + + item honorificSuffix { + type PolyString; + documentation """ + Honorific titles that go after the name. + + Examples: + - PhD. + - KBE + + This property is single-valued. If more + than one title is applicable, they have to be represented in + a single string (concatenated) form in the correct order. + """; + // UserType.honorificSuffix + // 160 + } + + item title { + type PolyString; + documentation """ + User's title defining a work position or a primary role in the + organization. + Examples: + - CEO + - Security Officer + - Assistant + """; + // UserType.title + // 170 + } + + item employeeNumber { + type string; + documentation """ + Unique, business-oriented identifier of the employee. + Typically used as correlation identifier and for + auditing purposes. Should be immutable, but the + specific properties and usage are deployment-specific. + """; + // UserType.employeeNumber + // 400 + } + + item employeeType { + type string; + maxOccurs unbounded; + documentation """ + Employee type specification such as internal employee, + external or partner. The specific values are + deployment-specific. However it is generally assumed that this + will be enumeration of several type names or codes that define + "classes" of users. + + Even though this property is named "employeeType" due to the historical + reasons it is used in a more generic way to mean general type of user. + Therefore it can be used to distinguish employees from customers, etc. + + DEPRECATED: Use ObjectType.subtype + """; + // UserType.employeeType + // 410 + // true + // 3.8 + } + item organization { + type PolyString; + maxOccurs unbounded; + documentation """ + Name or (preferably) immutable identifier of organization that the user belongs to. + The format is deployment-specific. This property together with organizationalUnit + may be used to provide easy-to-use data about organizational membership of the user. + + This is multi-valued property to allow membership of a user to several + organizations. Please note that midPoint does not maintain ordering in + multi-value properties therefore this is not usable to model a complex + organization hierarchies. Use OrgType instead. + """; + // UserType.organization + // 430 + } + + item organizationalUnit { + type PolyString; + maxOccurs unbounded; + documentation """ + Name or (preferably) immutable identifier of organizational unit that the user belongs to. + The format is deployment-specific. This property together with organization + may be used to provide easy-to-use data about organizational membership of the user. + + This is multi-valued property to allow membership of a user to several + organizational units. Please note that midPoint does not maintain ordering in + multi-value properties therefore this is not usable to model a complex + organization hierarchies. Use OrgType instead. + """; + // UserType.organizationalUnit + // 440 + } + item adminGuiConfiguration { + type AdminGuiConfiguration; + documentation """ + Specifies the admin GUI configuration that should be used + by this user. + """; + since "3.5"; + // AdminGuiConfigurationType.adminGuiConfiguration + } + } + + type ObjectReference { + objectReference; + documentation """ + Reference to an object. It contains OID of the object that it + refers to. + """; + + // item description; // TODO make this work + + // item documentation; // TODO make this work + + item filter { + type SearchFilter; // TODO namespace of "query-3" + documentation """ + Filter that can be used to dynamically lookup the reference OID e.g. during imports. + It must not be used for normal operations. The filter may be stored in the repository + to avoid data loss. But even if it is stored it will not be used beyond initial + import or unless explicitly requested (e.g. by setting resolutionTime). + + Note: The filter will NOT be used if the OID in the reference is set. The OID always takes + precedence. + """; + } + + item resolutionTime { + type EvaluationTime; + // TODO defaultValue "import"; + documentation """ + Definition of the "time" when the reference will be resolved. Resolving the reference means using + the filter to get object(s) or OID(s). + + Import-time resolution means that the reference will be resolved once when the file is imported. + OID will be recorded in the reference and then only the OID will be used to follow the reference. + This is a very efficient method and it is the default. + + Run-time resolution means that the reference will be resolved every time that the reference is + evaluated. This is less efficient but it provides great flexibility as the filter may contain + expressions and therefore the reference target may dynamically change. + """; + } + + item referentialIntegrity { + type ReferentialIntegrity; + // TODO defaultValue "default"; + documentation """ + Definition of the behavior related to non-existence of object with specified target OID. + (Currently supported only at selected places in midPoint.) + """; + since "4.1"; + } + + item targetName { + type PolyString; + documentation """ + Cached name of the target object. + This is a ephemeral value. It is not stored in the repository. + It may be computed at object retrieval time or it may not be present at all. + This is NOT an authoritative information. Setting it or changing it will + not influence the reference meaning. OID is the only authoritative linking + mechanism. + """; + } + + // TODO what about (attributes) oid, type, and relation? + // Should they be listed here even if they are defined in PrismReferenceValue? + // But if not, why should we list filter, resolution time, referential integrity here, + // as they are also defined in PrismReferenceValue. + } + + mixin Description { + item description { + type string; + documentation """ + Free-form textual description of the object. It is supposed to describe + the object or a construct that it is attached to. + + This information may be presented to midPoint users, even to ordinary end users. + For example role description may be presented to users when they are selecting + roles to request. Therefore the description should be written in a language that + the users can understand. + + Description is assumed to be a plan, non-formatted text. + Amount of white space is considered insignificant. E.g. leading and trailing + white space may be skipped, multiple spaces can be collapsed to one and so on. + """; + // ObjectType.description + // 10 + } + } + + mixin Documentation { + item documentation { + type string; + documentation """ + Technical documentation for a particular object or construct. + + The purpose of this element is to document system configuration and behavior. + The documentation will not be presented to end users. In fact, it will probably + not be presented at all in midPoint user interface. This documentation element + is supposed to be a part of the technical documentation of midPoint deployment. + The tools than generate deployment configuration will look for these elements + and combine them to compiled documentation document. + + AsciiDoc formatting is assumed for this element. Any leading or trailing + whitespace is skipped. Indentation equivalent to he indentation of the first + non-blank line of text is also skipped. + """; + // ObjectType.documentation + // 11 + } + } + + // Example of short version of container definition. + container { + name Assignment; + itemName assignment; + + // item description; // TODO make this work + // item documentation; // TODO make this work + // item extension; // TODO make this work + + // ... + } + + container { + name Extension; + itemName extension; + documentation """ + Extension container that provides generic extensibility mechanism. + Almost any extension property can be placed in this container. + This mechanism is used to extend objects with new properties. + The extension is treated exactly the same as other object + properties by the code (storage, modifications, etc), except + that the system may not be able to understand their meaning. + """; + // ObjectType.extension + // 1000 + } + + object GenericObject { + supertype Focus; + itemName genericObject; + documentation """ + Generic object for storing unknown (unexpected) object types. + + The generic object should be used if there is a need to + store a custom object (e.g KangarooType) at deployment-time. + The properties of such custom objects are to be placed in the + extension part of this object. The schema is not checked or + enforced for this type of objects if technically possible. + """; + item objectType { + type uri; // TODO + // deprecated + } + } + + container Trigger { + itemName trigger; + documentation """ + Defines triggers for an object. Trigger is an action that should take place + at specified time or under some other condition. + """; + item timestamp { + type dateTime; // TODO + documentation """ + The time when a trigger needs to be activated. + """; + } + item handlerUri { + type uri; // TODO + documentation """ + Handler URI indirectly specifies which class is responsible to handle the task. The handler will + to be used to handle trigger activation. + """; + } + item originDescription { + type string; + documentation """ + Short description of trigger origin, e.g. name of the mapping. + Used for diagnostic purposes. + """; + // true + since "4.0"; + } + item extension { // TODO + type Extension; + documentation """ + Extension container used to provide additional situation-specific information to the trigger. + """; + } + } + + container Metadata { + itemName metadata; + documentation """ + Meta-data about data creation, modification, etc. + It may apply to objects but also parts of the object (e.g. assignments). + + Meta-data only apply to successful operations. That is obvious for create, but it also applies + to modify. For obvious reasons there are no metadata about delete. + We keep no metadata about reading. That would be a huge performance hit. + + Meta-data only describe the last operation of its kind. E.g. there is a record of last + modification, last approval, etc. There is no history. The last operation overwrites data + about the previous operation. + + These data are informational only. They should not be used for security purposes (use auditing + subsystem for that). But presence of metadata simplifies system administration and may provide + some basic information "at the glance" which may be later confirmed by the audit logs. + + Meta-data are also supposed to be searchable. Therefore they may be used to quickly find + "candidate" objects for a closer examination. + """; + // true + // + // Metadata + + item requestTimestamp { + type dateTime; + documentation """ + The timestamp of "create" operation request. It is set once and should never be changed. + + In case of "background" processes to create object (e.g. create with approval) + this should be the timestamp when the process started. I.e. the timestamp when + the operation was requested. + """; + // MetadataType.requestTimestamp + // true + since "3.5"; + // + } + + item requestor { + type ObjectReference; + documentation """ + Reference to the user that requested the "create" operation for this object or assignment. + """; + // MetadataType.requestorRef + // true + // tns:UserType + // + } + + item requestorComment { + type string; + documentation """ + Comment of the user that requested the "create" operation for this object or assignment. + """; + // MetadataType.requestorComment + // true + since "3.7"; + } + + item createTimestamp { + type dateTime; + documentation """ + The timestamp of data creation. It is set once and should never be changed. + + In case of "background" processes to create object (e.g. create with approval) + this should be the timestamp when the process ended. I.e. the timestamp when + the operation was executed. + """; + // MetadataType.createTimestamp + // true + // true + since "3.5"; + } + + item creator { + type ObjectReference; + maxOccurs unbounded; + documentation """ + Reference to the user that approved the creation of the data (if there was such a user). + This is multi-value reference therefore multiple approvers may be recorded. However the order and + hierarchy of the approvers is lost. + """; + // MetadataType.createApproverRef + // true + // true + // tns:UserType + } + + item createApproverComment { + type string; + maxOccurs unbounded; + documentation """ + Comments of the approvers during the creation of the data. Note that these comments are in no + particular order, so basically it is not known who entered which comment. + """; + // MetadataType.createApprovalComment + // true + since "3.7"; + } + + item createApprovalTimestamp { + type dateTime; + documentation """ + The timestamp of creation approval. + """; + // MetadataType.createApprovalTimestamp + // true + since "3.5"; + } + + item createChannel { + type uri; + documentation """ + Channel in which the object was created. + """; + // MetadataType.createChannel + // true + // true + } + + item createTask { + type ObjectReference; + documentation """ + Reference to the task that created the object (if it was a persistent one). + """; + // MetadataType.createTaskRef + // true + // tns:TaskType + since "3.7"; + } + + item modifyTimestamp { + type dateTime; + // MetadataType.modifyTimestamp + // true + // true + } + + item modifier { + type ObjectReference; + // MetadataType.modifierRef + // true + // true + // tns:UserType + } + + item modifyApprover { + type ObjectReference; + maxOccurs unbounded; + // MetadataType.modifyApproverRef + // true + // true + // tns:UserType + } + + item modifyApprovalComment { + type string; + maxOccurs unbounded; + // MetadataType.modifyApprovalComment + // true + since "3.7"; + } + + item modifyChannel { + type uri; + // MetadataType.modifyChannel + // true + // true + } + + item modifyTask { + type ObjectReference; + // MetadataType.modifyTaskRef + // true + // tns:TaskType + since "3.7"; + } + + item lastProvisioningTimestamp { + type dateTime; + // MetadataType.lastProvisioningTimestamp + // true + since "3.6.1"; + } + + item certificationFinishedTimestamp { + type dateTime; + // MetadataType.certificationFinishedTimestamp + // true + since "3.7"; + } + + item certificationOutcome { + type string; + // MetadataType.certificationOutcome + // true + since "3.7"; + } + + item certifier { + type ObjectReference; + // MetadataType.certifierRef + // true + // tns:UserType + since "3.7"; + } + + item certifierComment { + type string; + maxOccurs unbounded; + // MetadataType.certifierComment + // true + since "3.7"; + } + + item originMappingName { + type string; + // MetadataType.originMappingName + // true + since "3.7"; + } + } + + // axiom + + // types-3 + type PolyString; + + // query-3 + type SearchFilter; + type EvaluationTime; + type ReferentialIntegrity; + + // ??? + // type Extension; + + // common-3 + type OperationResult; + type OperationExecution; + type LensContext; + type PolicyException; + type DiagnosticInformation; + type Credentials; + type AdminGuiConfiguration; + type Activation; + + // should exist because of "container XXX" + // Already defined + // type Trigger; + // type Assignment; + +} diff --git a/infra/axiom/src/test/resources/prism/prism-infra.axiom b/infra/axiom/src/test/resources/prism/prism-infra.axiom new file mode 100644 index 00000000000..6a5b40b1907 --- /dev/null +++ b/infra/axiom/src/test/resources/prism/prism-infra.axiom @@ -0,0 +1,10 @@ +model test { + @type prism:PrismModel; + namespace "http://example.org"; + import prism { + namespace "http://midpoint.evolveum.com/xml/ns/public/common/prism"; + } + container test { + + } +} \ No newline at end of file diff --git a/infra/axiom/src/test/resources/prism/prism.axiom b/infra/axiom/src/test/resources/prism/prism.axiom new file mode 100644 index 00000000000..e066455b3b8 --- /dev/null +++ b/infra/axiom/src/test/resources/prism/prism.axiom @@ -0,0 +1,76 @@ +model prism { + + namespace "http://midpoint.evolveum.com/xml/ns/public/common/prism"; + + import "https://schema.evolveum.com/ns/axiom/model" { + prefix axiom; + } + + root model { + type PrismModel; + } + + augmentation PrismModelExtension { + target axiom:AxiomModel; + + item object { + type PrismObjectDefinition; + } + + item container { + type PrismContainerDefinition; + } + + item reference { + type PrismReferenceDefinition; + } + } + + type PrismModel { + supertype axiom:AxiomModel; + item type { // {}:type + type PrismTypeDefinition; + } + } + + augmentation PrismTypeDefinitionAnnotation { + target axiom:AxiomTypeDefinition; + // TODO move to prism schema + item object { + type boolean; + } + + // TODO move to prism schema + item container { + type boolean; + } + // TODO move to prism schema + item objectReference { + type boolean; + } + item itemName { + type axiom:AxiomName; + } + } + + type PrismTypeDefinition { + supertype axiom:AxiomTypeDefinition; + } + + type PrismObjectDefinition { + supertype PrismTypeDefinition; + } + + type PrismContainerDefinition { + supertype PrismTypeDefinition; + } + + type PrismReferenceDefinition { + supertype PrismTypeDefinition; + } + + type PrismItemDefinition { + supertype PrismTypeDefinition; + } + +} diff --git a/infra/axiom/src/test/resources/scripting.axiom b/infra/axiom/src/test/resources/scripting.axiom index c484847ddc1..1eac0017932 100644 --- a/infra/axiom/src/test/resources/scripting.axiom +++ b/infra/axiom/src/test/resources/scripting.axiom @@ -65,7 +65,7 @@ model scripting { } type ExpressionPipeline { - extends ScriptingExpression; + supertype ScriptingExpression; documentation """ Pipeline of expressions - they are executed one after another, input sent to the pipeline as a whole is sent to the first expression. @@ -85,7 +85,7 @@ model scripting { } type ExpressionSequence { - extends ScriptingExpression; + supertype ScriptingExpression; documentation """ Sequence of expressions - they are executed one after another, input sent to the sequence as a whole is then sent individually @@ -104,7 +104,7 @@ model scripting { } type SearchExpression { - extends ScriptingExpression; + supertype ScriptingExpression; documentation """ Queries the model for objects of a given type, optionally fulfilling given condition. """;