diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/ReferenceConverter.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/ReferenceConverter.java index 2af853ffdab..99aae49d14f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/ReferenceConverter.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/autocomplete/ReferenceConverter.java @@ -57,7 +57,7 @@ public ObjectReferenceType convertToObject(String value, Locale locale) throws C if (CollectionUtils.isNotEmpty(objectsList)){ return ObjectTypeUtil.createObjectRefWithFullObject(objectsList.get(0), pageBase.getPrismContext()); } - return originConverter.convertToObject(value, locale); + return null; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java index fb5e3c5faf6..95b14014609 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java @@ -4396,6 +4396,20 @@ public static Class resolveSelfPage() { return null; } + public static PrismObject findLookupTable(ItemDefinition definition, PageBase page) { + PrismReferenceValue valueEnumerationRef = definition.getValueEnumerationRef(); + if (valueEnumerationRef == null) { + return null; + } + + String lookupTableUid = valueEnumerationRef.getOid(); + Task task = page.createSimpleTask("loadLookupTable"); + OperationResult result = task.getResult(); + + Collection> options = WebModelServiceUtils.createLookupTableRetrieveOptions(page.getSchemaHelper()); + return WebModelServiceUtils.loadObject(LookupTableType.class, lookupTableUid, options, page, task, result); + } + public static boolean hasAnyArchetypeAssignemnt(AH assignmentHolder) { if (assignmentHolder.getAssignment() == null) { return false; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/SearchFilterPanelFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/SearchFilterPanelFactory.java index 1d543adc758..07636e1ac46 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/SearchFilterPanelFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/panel/SearchFilterPanelFactory.java @@ -9,6 +9,15 @@ import javax.annotation.PostConstruct; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; + +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.web.page.admin.reports.component.SearchFilterConfigurationPanel; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectCollectionType; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + import org.apache.wicket.markup.html.panel.Panel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -34,6 +43,14 @@ public boolean match(IW wrapper) { @Override protected Panel getPanel(PrismPropertyPanelContext panelCtx) { + PrismPropertyWrapper searchFilterItemWrapper = panelCtx.unwrapWrapperModel(); + PrismContainerValueWrapper containerWrapper = searchFilterItemWrapper.getParent(); + if (containerWrapper != null && containerWrapper.getRealValue() instanceof ObjectCollectionType){ + ObjectCollectionType collectionObj = (ObjectCollectionType) containerWrapper.getRealValue(); + return new SearchFilterConfigurationPanel(panelCtx.getComponentId(), panelCtx.getRealValueModel(), + (Class)WebComponentUtil.qnameToClass(panelCtx.getPageBase().getPrismContext(), + collectionObj.getType() != null ? collectionObj.getType() : ObjectType.COMPLEX_TYPE)); + } return new AceEditorPanel(panelCtx.getComponentId(), null, new SearchFilterTypeModel(panelCtx.getRealValueModel(), panelCtx.getPageBase()), 10); } 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 f0975820a2c..ce0f1d2c934 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 @@ -81,6 +81,9 @@ public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper } protected List getItemDefinitions(PrismContainerWrapper parent, PrismContainerValue value) { + if (parent == null){ + return new ArrayList<>(); + } return parent.getDefinitions(); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/input/DataLanguagePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/input/DataLanguagePanel.java index ef022b44867..5c0496dd04b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/input/DataLanguagePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/input/DataLanguagePanel.java @@ -80,7 +80,7 @@ protected void onStateChanged(int updatedIndex, AjaxRequestTarget target) { @SuppressWarnings({ "unchecked", "raw" }) List> list = (List>) updatedObject; if (list.size() != 1) { - updatedObjectString = serializer.serializeObjects(list, null); + updatedObjectString = serializer.serializeObjects(list); } else { updatedObjectString = serializer.serialize(list.get(0)); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.html index c58bfdb42f3..2c4e6a63384 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.html @@ -13,8 +13,9 @@ diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.java index 315371d54d8..be0f2b44ab1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/AbstractSearchConfigurationPanel.java @@ -6,6 +6,10 @@ */ package com.evolveum.midpoint.web.component.search; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.web.component.search.filter.SearchFilter; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; + import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.model.IModel; @@ -20,19 +24,23 @@ /** * @author Kateryna Honchar */ -public abstract class AbstractSearchConfigurationPanel extends BasePanel { +public abstract class AbstractSearchConfigurationPanel extends BasePanel { private static final long serialVersionUID = 1L; private static final Trace LOG = TraceManager.getTrace(AbstractSearchConfigurationPanel.class); protected static final String ID_CONFIGURATION_PANEL = "configurationPanel"; private static final String ID_BUTTONS_PANEL = "buttonsPanel"; - private static final String ID_APPLY_FILTER_BUTTON = "applyFilterButton"; - private static final String ID_SAVE_FILTER_BUTTON = "saveFilterButton"; + private static final String ID_OK_BUTTON = "okButton"; +// private static final String ID_APPLY_FILTER_BUTTON = "applyFilterButton"; +// private static final String ID_SAVE_FILTER_BUTTON = "saveFilterButton"; private static final String ID_CANCEL_BUTTON = "cancelButton"; - public AbstractSearchConfigurationPanel(String id, IModel searchModel) { + private Class type; + + public AbstractSearchConfigurationPanel(String id, IModel searchModel, Class type) { super(id, searchModel); + this.type = type; } @Override @@ -56,27 +64,38 @@ private void initButtonsPanel() { buttonsPanel.setOutputMarkupId(true); add(buttonsPanel); - AjaxButton applyFilterButton = new AjaxButton(ID_APPLY_FILTER_BUTTON, createStringResource("SearchPropertiesConfigPanel.applyFilterButton")) { + AjaxButton applyFilterButton = new AjaxButton(ID_OK_BUTTON, createStringResource("Button.ok")) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget ajaxRequestTarget) { - //todo + okButtonClicked(ajaxRequestTarget); } }; applyFilterButton.setOutputMarkupId(true); buttonsPanel.add(applyFilterButton); - AjaxButton saveFilterButton = new AjaxButton(ID_SAVE_FILTER_BUTTON, createStringResource("SearchPropertiesConfigPanel.saveFilterButton")) { - private static final long serialVersionUID = 1L; - - @Override - public void onClick(AjaxRequestTarget ajaxRequestTarget) { - //todo - } - }; - saveFilterButton.setOutputMarkupId(true); - buttonsPanel.add(saveFilterButton); +// AjaxButton applyFilterButton = new AjaxButton(ID_APPLY_FILTER_BUTTON, createStringResource("SearchPropertiesConfigPanel.applyFilterButton")) { +// private static final long serialVersionUID = 1L; +// +// @Override +// public void onClick(AjaxRequestTarget ajaxRequestTarget) { +// //todo +// } +// }; +// applyFilterButton.setOutputMarkupId(true); +// buttonsPanel.add(applyFilterButton); +// +// AjaxButton saveFilterButton = new AjaxButton(ID_SAVE_FILTER_BUTTON, createStringResource("SearchPropertiesConfigPanel.saveFilterButton")) { +// private static final long serialVersionUID = 1L; +// +// @Override +// public void onClick(AjaxRequestTarget ajaxRequestTarget) { +// //todo +// } +// }; +// saveFilterButton.setOutputMarkupId(true); +// buttonsPanel.add(saveFilterButton); AjaxButton cancelButton = new AjaxButton(ID_CANCEL_BUTTON, createStringResource("SearchPropertiesConfigPanel.cancelButton")) { private static final long serialVersionUID = 1L; @@ -92,6 +111,11 @@ public void onClick(AjaxRequestTarget ajaxRequestTarget) { protected abstract void initConfigurationPanel(WebMarkupContainer configPanel); - @NotNull - protected abstract Class getObjectClass(); + protected void okButtonClicked(AjaxRequestTarget target){ + + } + + public Class getType() { + return type; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/BasicSearchFilterModel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/BasicSearchFilterModel.java new file mode 100644 index 00000000000..487b3aaf34b --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/BasicSearchFilterModel.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2019 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.web.component.search; + +import com.evolveum.midpoint.gui.api.page.PageBase; +import com.evolveum.midpoint.gui.impl.factory.panel.SearchFilterTypeModel; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.LoggingUtils; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.web.component.search.filter.BasicSearchFilter; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; + +import org.apache.commons.lang3.StringUtils; +import org.apache.wicket.model.IModel; + +/** + * @author honchar + */ +public class BasicSearchFilterModel implements IModel> { + + private static final Trace LOGGER = TraceManager.getTrace(SearchFilterTypeModel.class); + + private static final long serialVersionUID = 1L; + + private IModel baseModel; + private PageBase pageBase; + private Class type; + private BasicSearchFilter basicSearchFilter; + + public BasicSearchFilterModel(IModel valueWrapper, Class type, PageBase pageBase) { + this.baseModel = valueWrapper; + this.pageBase = pageBase; + this.type = type; + } + + @Override + public void detach() { + // TODO Auto-generated method stub + + } + + @Override + public BasicSearchFilter getObject() { + if (basicSearchFilter == null){ + basicSearchFilter = loadBasicSearchFilter(); + } + return basicSearchFilter; + } + + private BasicSearchFilter loadBasicSearchFilter(){ + try { +// SearchFilterType value = baseModel.getObject(); +// if (value == null) { +// return new BasicSearchFilter(pageBase.getPrismContext(), null, type); +// } + + ObjectFilter objectFilter = pageBase.getPrismContext().getQueryConverter().createObjectFilter(type, baseModel.getObject()); + return new BasicSearchFilter(pageBase.getPrismContext(), objectFilter, type); + } catch (SchemaException e) { + // TODO handle + LoggingUtils.logUnexpectedException(LOGGER, "Cannot serialize filter", e); + } + return null; + } + + @Override + public void setObject(BasicSearchFilter object) { + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceAutocomplete.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceAutocomplete.java index 0a9e60b2233..8debf2132ff 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceAutocomplete.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceAutocomplete.java @@ -20,6 +20,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.apache.commons.collections.iterators.ArrayIterator; import org.apache.commons.lang.StringUtils; import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer; import org.apache.wicket.markup.html.form.FormComponent; @@ -49,7 +50,7 @@ public Iterator getIterator(String input) { FormComponent inputField = getBaseFormComponent(); String realInput = StringUtils.isEmpty(input) ? inputField.getRawInput() : input; if (StringUtils.isEmpty(realInput)){ - return null; + return new ArrayIterator(); } ObjectQuery query = pageBase.getPrismContext().queryFor(AbstractRoleType.class) .item(ObjectType.F_NAME) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html new file mode 100644 index 00000000000..c29d926821b --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.html @@ -0,0 +1,27 @@ + + + + + +
+
+
+ + + +
+
+ +
+
+
+
+
+
+ + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.java new file mode 100644 index 00000000000..4f5b9244e26 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPanel.java @@ -0,0 +1,142 @@ +/* + * 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.web.component.search; + +import com.evolveum.midpoint.gui.api.component.BasePanel; +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.prism.PrismReferenceDefinition; +import com.evolveum.midpoint.web.component.AjaxButton; +import com.evolveum.midpoint.web.component.input.TextPanel; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.AreaCategoryType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; + +import org.apache.commons.lang.StringUtils; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; + +import javax.xml.namespace.QName; +import java.util.List; + +/** + * @author honchar + */ +public class ReferenceValueSearchPanel extends BasePanel { + + private static final long serialVersionUID = 1L; + + private static final String ID_REFERENCE_VALUE_TEXT_FIELD = "referenceValueTextField"; + private static final String ID_EDIT_BUTTON = "editReferenceButton"; + private static final String ID_REF_POPOVER_PANEL = "refPopoverPanel"; + private static final String ID_REF_POPOVER_BODY = "refPopoverBody"; + private static final String ID_REF_POPOVER = "refPopover"; + private PrismReferenceDefinition referenceDef; + + public ReferenceValueSearchPanel(String id, IModel model, PrismReferenceDefinition referenceDef) { + super(id, model); + this.referenceDef = referenceDef; + } + + @Override + protected void onInitialize(){ + super.onInitialize(); + initLayout(); + } + + private void initLayout(){ + setOutputMarkupId(true); + + TextPanel referenceTextValueField = new TextPanel(ID_REFERENCE_VALUE_TEXT_FIELD, Model.of(getReferenceTextModel())); + referenceTextValueField.setOutputMarkupId(true); + referenceTextValueField.setEnabled(false); + add(referenceTextValueField); + + AjaxButton editButton = new AjaxButton(ID_EDIT_BUTTON) { + private static final long serialVersionUID = 1L; + + @Override + public void onClick(AjaxRequestTarget target) { + togglePopover(target, ReferenceValueSearchPanel.this.get(ID_REFERENCE_VALUE_TEXT_FIELD), + ReferenceValueSearchPanel.this.get(createComponentPath(ID_REF_POPOVER)), 0); + } + }; + editButton.setOutputMarkupId(true); + add(editButton); + + WebMarkupContainer popover = new WebMarkupContainer(ID_REF_POPOVER); + popover.setOutputMarkupId(true); + add(popover); + + WebMarkupContainer popoverBody = new WebMarkupContainer(ID_REF_POPOVER_BODY); + popoverBody.setOutputMarkupId(true); + popover.add(popoverBody); + + ReferenceValueSearchPopupPanel value = + new ReferenceValueSearchPopupPanel(ID_REF_POPOVER_PANEL, getModel()) { + + private static final long serialVersionUID = 1L; + + @Override + protected List getAllowedRelations() { + return WebComponentUtil.getCategoryRelationChoices(AreaCategoryType.ADMINISTRATION, getPageBase()); + } + + @Override + protected List getSupportedTargetList() { + return WebComponentUtil.createSupportedTargetTypeList((referenceDef).getTargetTypeName()); + } + + @Override + protected void confirmPerformed(AjaxRequestTarget target) { + target.add(ReferenceValueSearchPanel.this); + } + }; + value.setRenderBodyOnly(true); + popoverBody.add(value); + + } + + private String getReferenceTextModel(){ + ObjectReferenceType ref = getModelObject(); + if (ref == null){ + return null; + } + StringBuilder sb = new StringBuilder(); + if (StringUtils.isNotEmpty(ref.getOid())){ + sb.append(createStringResource("ReferencePopupPanel.oid").getString()); + sb.append(ref.getOid()); + } + if (ref.getRelation() != null){ + if (sb.length() > 0){ + sb.append("; "); + } + sb.append(createStringResource("ReferencePopupPanel.relation").getString()); + sb.append(ref.getRelation().getLocalPart()); + } + if (ref.getType() != null){ + if (sb.length() > 0){ + sb.append("; "); + } + sb.append(ref.getType().getLocalPart()); + } + return sb.toString(); + } + + public void togglePopover(AjaxRequestTarget target, Component button, Component popover, int paddingRight) { + StringBuilder script = new StringBuilder(); + script.append("toggleSearchPopover('"); + script.append(button.getMarkupId()).append("','"); + script.append(popover.getMarkupId()).append("',"); + script.append(paddingRight).append(");"); + + target.appendJavaScript(script.toString()); + } + +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferencePopupPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPopupPanel.html similarity index 100% rename from gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferencePopupPanel.html rename to gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPopupPanel.html diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferencePopupPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPopupPanel.java similarity index 80% rename from gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferencePopupPanel.java rename to gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPopupPanel.java index d9553b9d27d..1f7867cbf57 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferencePopupPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/ReferenceValueSearchPopupPanel.java @@ -10,8 +10,10 @@ import java.util.List; import javax.xml.namespace.QName; +import com.evolveum.midpoint.gui.api.component.BasePanel; import com.evolveum.midpoint.gui.api.component.autocomplete.AutoCompleteReferenceRenderer; +import com.evolveum.midpoint.web.component.util.VisibleBehaviour; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import org.apache.commons.collections.CollectionUtils; @@ -32,7 +34,7 @@ import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnBlurAjaxFormUpdatingBehaviour; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -public class ReferencePopupPanel extends SearchPopupPanel { +public class ReferenceValueSearchPopupPanel extends BasePanel { private static final long serialVersionUID = 1L; @@ -42,11 +44,8 @@ public class ReferencePopupPanel extends SearchPopupPanel< private static final String ID_RELATION = "relation"; private static final String ID_CONFIRM_BUTTON = "confirmButton"; -// private List allowedRelations; - - public ReferencePopupPanel(String id, IModel> model) { + public ReferenceValueSearchPopupPanel(String id, IModel model) { super(id, model); -// this.allowedRelations = allowedRelations; } @Override @@ -56,9 +55,9 @@ protected void onInitialize() { } private void initLayout() { + setOutputMarkupId(true); - TextField oidField = new TextField(ID_OID, new PropertyModel<>(getModel(), SearchValue.F_VALUE + ".oid")); - + TextField oidField = new TextField(ID_OID, new PropertyModel<>(getModel(), "oid")); oidField.add(new AjaxFormComponentUpdatingBehavior("blur") { private static final long serialVersionUID = 1L; @@ -69,10 +68,11 @@ protected void onUpdate(AjaxRequestTarget target) { } }); oidField.setOutputMarkupId(true); + oidField.add(new VisibleBehaviour(() -> true)); oidField.add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); add(oidField); - ReferenceAutocomplete nameField = new ReferenceAutocomplete(ID_NAME, Model.of(getModelObject().getValue()), + ReferenceAutocomplete nameField = new ReferenceAutocomplete(ID_NAME, Model.of(getModelObject()), new AutoCompleteReferenceRenderer(), getPageBase()){ @@ -89,20 +89,22 @@ protected Class getReferenceTargetObjectType(){ }; -// nameField.add(new AjaxFormComponentUpdatingBehavior("blur") { -// -// private static final long serialVersionUID = 1L; -// -// @Override -// protected void onUpdate(AjaxRequestTarget target) { -// -// } -// }); + nameField.getBaseFormComponent().add(new AjaxFormComponentUpdatingBehavior("blur") { + + private static final long serialVersionUID = 1L; + + @Override + protected void onUpdate(AjaxRequestTarget target) { + ObjectReferenceType ort = nameField.getBaseFormComponent().getModelObject(); + ReferenceValueSearchPopupPanel.this.getModel().setObject(ort); + target.add(ReferenceValueSearchPopupPanel.this.get(ID_OID)); + } + }); nameField.setOutputMarkupId(true); nameField.getBaseFormComponent().add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); add(nameField); - DropDownChoice type = new DropDownChoice<>(ID_TYPE, new PropertyModel(getModel(), SearchValue.F_VALUE + ".type"), + DropDownChoice type = new DropDownChoice<>(ID_TYPE, new PropertyModel(getModel(), "type"), getSupportedTargetList(), new QNameObjectTypeChoiceRenderer()); type.setNullValid(true); type.setOutputMarkupId(true); @@ -118,15 +120,15 @@ public boolean isEnabled() { type.add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); add(type); - if (getModelObject() != null && getModelObject().getValue() != null && getModelObject().getValue().getRelation() == null){ - getModelObject().getValue().setRelation(PrismConstants.Q_ANY); + if (getModelObject() != null && getModelObject().getRelation() == null){ + getModelObject().setRelation(PrismConstants.Q_ANY); } List allowedRelations = new ArrayList(); allowedRelations.addAll(getAllowedRelations()); if (!allowedRelations.contains(PrismConstants.Q_ANY)) { allowedRelations.add(0, PrismConstants.Q_ANY); } - DropDownChoice relation = new DropDownChoice<>(ID_RELATION, new PropertyModel(getModel(), SearchValue.F_VALUE + ".relation"), + DropDownChoice relation = new DropDownChoice<>(ID_RELATION, new PropertyModel(getModel(), "relation"), allowedRelations, new QNameObjectTypeChoiceRenderer()); // relation.setNullValid(true); relation.setOutputMarkupId(true); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java index 3fc4e1d13e8..c317cd3b795 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchItemPanel.java @@ -15,17 +15,14 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.gui.api.component.autocomplete.AutoCompleteTextPanel; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import org.apache.commons.lang.StringUtils; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; -import org.apache.wicket.ajax.AjaxEventBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.behavior.Behavior; -import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.IChoiceRenderer; @@ -195,10 +192,9 @@ private void initSearchItemField(WebMarkupContainer searchItemContainer) { Component searchItemField = null; SearchItem item = getModelObject(); IModel>> choices = null; - PrismObject lookupTable = findLookupTable(item.getDefinition()); + PrismObject lookupTable = WebComponentUtil.findLookupTable(item.getDefinition(), getPageBase()); switch (item.getType()) { case REFERENCE: - //TODO change probably to another component searchItemField = new TextPanel(ID_SEARCH_ITEM_FIELD, new PropertyModel(getModel(), "value.value"){ private static final long serialVersionUID = 1L; @@ -355,21 +351,9 @@ private void initPopover() { popoverBody.setOutputMarkupId(true); popover.add(popoverBody); - - DisplayableValue val = SearchItemPanel.this.getModelObject().getValue(); - IModel> refModel = new IModel>() { - @Override - public DisplayableValue getObject() { - if (val.getValue() instanceof ObjectReferenceType){ - return val; - } else { - return null; - } - } - }; if (getModelObject() != null && SearchItem.Type.REFERENCE.equals(getModelObject().getType())) { - SearchPopupPanel value = - (SearchPopupPanel) new ReferencePopupPanel(ID_VALUE, new PropertyModel<>(getModel(), "value")) { + ReferenceValueSearchPopupPanel value = + new ReferenceValueSearchPopupPanel(ID_VALUE, new PropertyModel<>(getModel(), "value.value")) { private static final long serialVersionUID = 1L; @@ -403,22 +387,6 @@ protected void confirmPerformed(AjaxRequestTarget target) { } } - private PrismObject findLookupTable(ItemDefinition definition) { - PrismReferenceValue valueEnumerationRef = definition.getValueEnumerationRef(); - if (valueEnumerationRef == null) { - return null; - } - - PageBase page = getPageBase(); - - String lookupTableUid = valueEnumerationRef.getOid(); - Task task = page.createSimpleTask("loadLookupTable"); - OperationResult result = task.getResult(); - - Collection> options = WebModelServiceUtils.createLookupTableRetrieveOptions(getSchemaHelper()); - return WebModelServiceUtils.loadObject(LookupTableType.class, lookupTableUid, options, page, task, result); - } - private IModel>> createBooleanChoices() { List> list = new ArrayList<>(); list.add(new SearchValue<>(Boolean.TRUE, getString("Boolean.TRUE"))); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java index 1ccb1e72f90..da75d1b865b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPanel.java @@ -785,14 +785,14 @@ private boolean isFullTextSearchEnabled(){ } private void searchConfigurationPerformed(AjaxRequestTarget target){ - SearchPropertiesConfigPanel configPanel = new SearchPropertiesConfigPanel(getPageBase().getMainPopupBodyId(), getModel()) { - private static final long serialVersionUID = 1L; - - @Override - protected @NotNull Class getObjectClass() { - return SearchPanel.this.getModelObject().getType(); - } - }; - getPageBase().showMainPopup(configPanel, target); +// SearchPropertiesConfigPanel configPanel = new SearchPropertiesConfigPanel(getPageBase().getMainPopupBodyId(), getModel()) { +// private static final long serialVersionUID = 1L; +// +// @Override +// protected @NotNull Class getObjectClass() { +// return SearchPanel.this.getModelObject().getType(); +// } +// }; +// getPageBase().showMainPopup(configPanel, target); } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.html index f1b1160229d..6521ead6309 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.html @@ -11,10 +11,6 @@
- - - -
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.java index 2965f419c92..81537fe87b7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/SearchPropertiesConfigPanel.java @@ -8,8 +8,10 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -26,9 +28,14 @@ import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.StringResourceModel; +import com.evolveum.midpoint.gui.api.component.autocomplete.AutoCompleteTextPanel; import com.evolveum.midpoint.gui.api.model.LoadableModel; +import com.evolveum.midpoint.gui.api.page.PageBase; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.util.DisplayableValue; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.web.component.AjaxButton; import com.evolveum.midpoint.web.component.data.BoxedTablePanel; import com.evolveum.midpoint.web.component.data.column.CheckBoxColumn; @@ -36,39 +43,45 @@ import com.evolveum.midpoint.web.component.dialog.Popupable; import com.evolveum.midpoint.web.component.input.DropDownChoicePanel; import com.evolveum.midpoint.web.component.input.TextPanel; +import com.evolveum.midpoint.web.component.prism.InputPanel; +import com.evolveum.midpoint.web.component.search.filter.BasicSearchFilter; +import com.evolveum.midpoint.web.component.search.filter.ValueSearchFilterItem; +import com.evolveum.midpoint.web.component.util.EnableBehaviour; import com.evolveum.midpoint.web.component.util.SelectableBean; import com.evolveum.midpoint.web.component.util.SelectableListDataProvider; import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnBlurAjaxFormUpdatingBehaviour; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** * @author Kateryna Honchar */ -public abstract class SearchPropertiesConfigPanel extends AbstractSearchConfigurationPanel implements Popupable { +public class SearchPropertiesConfigPanel extends AbstractSearchConfigurationPanel, O> implements Popupable { private static final long serialVersionUID = 1L; private static final String ID_PROPERTY_CONFIG_CONTAINER = "propertyConfigContainer"; private static final String ID_PROPERTY_CHOICE = "propertyChoice"; - private static final String ID_PROPERTY_VALUE = "propertyValue"; - private static final String ID_FILTER = "filter"; - private static final String ID_MATCHING_RULE = "matchingRule"; - private static final String ID_NEGATION = "negation"; private static final String ID_PROPERTIES_TABLE = "propertiesTable"; private static final String ID_ADD_BUTTON = "addButton"; - IModel propertyChoiceModel = Model.of(); + private SelectableListDataProvider, ValueSearchFilterItem> provider; - public SearchPropertiesConfigPanel(String id, IModel searchModel) { - super(id, searchModel); + public SearchPropertiesConfigPanel(String id, IModel> searchModel, Class type) { + super(id, searchModel, type); } @Override protected void initConfigurationPanel(WebMarkupContainer configPanel) { + provider = + new SelectableListDataProvider, ValueSearchFilterItem>(getPageBase(), getSearchFilterItemModel()); + WebMarkupContainer propertyConfigContainer = new WebMarkupContainer(ID_PROPERTY_CONFIG_CONTAINER); propertyConfigContainer.setOutputMarkupId(true); configPanel.add(propertyConfigContainer); DropDownChoicePanel propertyChoicePanel = new DropDownChoicePanel(ID_PROPERTY_CHOICE, - propertyChoiceModel, Model.ofList(getAvailablePropertiesList()), new IChoiceRenderer() { + Model.of(getDefaultPropertyChoice()), getAvailablePropertiesListModel(), new IChoiceRenderer() { private static final long serialVersionUID = 1L; @@ -98,26 +111,6 @@ protected String getNullValidDisplayValue() { propertyChoicePanel.getBaseFormComponent().add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); propertyConfigContainer.add(propertyChoicePanel); -// TextPanel valuePanel = new TextPanel(ID_PROPERTY_VALUE, Model.of()); -// valuePanel.setOutputMarkupId(true); -// propertyConfigContainer.add(valuePanel); -// -// DropDownChoicePanel filterChoice = WebComponentUtil.createEnumPanel(ID_FILTER, -// Model.ofList(Arrays.asList(SearchConfigDto.FilterType.values())), Model.of(), -// SearchPropertiesConfigPanel.this, true, "Select"); //todo allow null? -// filterChoice.setOutputMarkupId(true); -// propertyConfigContainer.add(filterChoice); -// -// DropDownChoicePanel matchingRuleChoice = WebComponentUtil.createEnumPanel(ID_MATCHING_RULE, -// Model.ofList(Arrays.asList(SearchConfigDto.MatchingRule.values())), Model.of(), -// SearchPropertiesConfigPanel.this, true, "Select"); //todo allow null? -// matchingRuleChoice.setOutputMarkupId(true); -// propertyConfigContainer.add(matchingRuleChoice); -// -// CheckBoxPanel negationChoice = new CheckBoxPanel(ID_NEGATION, Model.of()); -// negationChoice.setOutputMarkupId(true); -// propertyConfigContainer.add(negationChoice); - AjaxButton addButton = new AjaxButton(ID_ADD_BUTTON) { private static final long serialVersionUID = 1L; @@ -132,12 +125,18 @@ public void onClick(AjaxRequestTarget ajaxRequestTarget) { initTable(configPanel); } + private Property getDefaultPropertyChoice() { + List availablePropertiesList = getAvailablePropertiesListModel().getObject(); + if (CollectionUtils.isNotEmpty(availablePropertiesList)) { + return availablePropertiesList.get(0); + } + return null; + } + private void initTable(WebMarkupContainer configPanel) { - SelectableListDataProvider, SearchConfigDto> provider = - new SelectableListDataProvider, SearchConfigDto>(getPageBase(), getSearchConfigModel()); - List, String>> columns = getTableColumns(); - BoxedTablePanel> table = - new BoxedTablePanel>(ID_PROPERTIES_TABLE, provider, columns, null, 20) { + List, String>> columns = getTableColumns(); + BoxedTablePanel> table = + new BoxedTablePanel>(ID_PROPERTIES_TABLE, provider, columns, null, 20) { private static final long serialVersionUID = 1L; @Override @@ -182,62 +181,61 @@ public boolean isAutoRefreshEnabled() { } }; table.setOutputMarkupId(true); - configPanel.addOrReplace(table); + configPanel.add(table); } - private List, String>> getTableColumns() { - List, String>> columns = new ArrayList<>(); + private List, String>> getTableColumns() { + List, String>> columns = new ArrayList<>(); - CheckBoxHeaderColumn> checkboxColumn = new CheckBoxHeaderColumn<>(); + CheckBoxHeaderColumn> checkboxColumn = new CheckBoxHeaderColumn<>(); columns.add(checkboxColumn); - IColumn, String> propertyColumn = new PropertyColumn, String>(getPageBase() + IColumn, String> propertyColumn = new PropertyColumn, String>(getPageBase() .createStringResource("SearchPropertiesConfigPanel.table.column.property"), - "value." + SearchConfigDto.F_PROPERTY + "." + Property.F_NAME); + "value." + ValueSearchFilterItem.F_PROPERTY_NAME); columns.add(propertyColumn); - IColumn, String> valueColumn = new AbstractColumn, String>(getPageBase() + IColumn, String> valueColumn = new AbstractColumn, String>(getPageBase() .createStringResource("SearchPropertiesConfigPanel.table.column.value")) { private static final long serialVersionUID = 1L; @Override - public void populateItem(Item>> item, String id, IModel> rowModel) { - TextPanel valuePanel = new TextPanel(id, new PropertyModel(rowModel, "value." + SearchConfigDto.F_VALUE)); - valuePanel.setOutputMarkupId(true); - valuePanel.getBaseFormComponent().add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); - item.add(valuePanel); + public void populateItem(Item>> item, String id, IModel> rowModel) { + item.add(getPropertyValueField( id, rowModel)); } }; columns.add(valueColumn); - IColumn, String> filterColumn = new AbstractColumn, String>(getPageBase() + IColumn, String> filterColumn = new AbstractColumn, String>(getPageBase() .createStringResource("SearchPropertiesConfigPanel.table.column.filter")) { private static final long serialVersionUID = 1L; @Override - public void populateItem(Item>> item, String id, IModel> rowModel) { - DropDownChoicePanel filterPanel = WebComponentUtil.createEnumPanel(id, - Model.ofList(Arrays.asList(SearchConfigDto.FilterType.values())), - new PropertyModel<>(rowModel, "value." + SearchConfigDto.F_FILTER_TYPE), - SearchPropertiesConfigPanel.this, true, + public void populateItem(Item>> item, String id, IModel> rowModel) { + List availableFilterNames = rowModel.getObject().getValue().getAvailableFilterNameList(); + DropDownChoicePanel filterPanel = WebComponentUtil.createEnumPanel(id, + Model.ofList(availableFilterNames), + new PropertyModel<>(rowModel, "value." + ValueSearchFilterItem.F_FILTER_TYPE_NAME), + SearchPropertiesConfigPanel.this, false, getPageBase().createStringResource("SearchPropertiesConfigPanel.selectFilter").getString()); filterPanel.setOutputMarkupId(true); filterPanel.getBaseFormComponent().add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); + filterPanel.getBaseFormComponent().add(new EnableBehaviour(() -> availableFilterNames.size() > 1)); item.add(filterPanel); } }; columns.add(filterColumn); - IColumn, String> matchingRuleColumn = new AbstractColumn, String>(getPageBase() + IColumn, String> matchingRuleColumn = new AbstractColumn, String>(getPageBase() .createStringResource("SearchPropertiesConfigPanel.table.column.matchingRule")) { private static final long serialVersionUID = 1L; @Override - public void populateItem(Item>> item, String id, IModel> rowModel) { - DropDownChoicePanel matchingRulePanel = WebComponentUtil.createEnumPanel(id, - Model.ofList(Arrays.asList(SearchConfigDto.FilterType.values())), - new PropertyModel<>(rowModel, "value." + SearchConfigDto.F_MATCHING_RULE), + public void populateItem(Item>> item, String id, IModel> rowModel) { + DropDownChoicePanel matchingRulePanel = WebComponentUtil.createEnumPanel(id, + Model.ofList(Arrays.asList(ValueSearchFilterItem.MatchingRule.values())), + new PropertyModel<>(rowModel, "value." + ValueSearchFilterItem.F_MATCHING_RULE), SearchPropertiesConfigPanel.this, true, getPageBase().createStringResource("SearchPropertiesConfigPanel.selectMatchingRule").getString()); matchingRulePanel.setOutputMarkupId(true); @@ -247,50 +245,143 @@ public void populateItem(Item>> i }; columns.add(matchingRuleColumn); - CheckBoxColumn> negationColumn = new CheckBoxColumn>(getPageBase() + CheckBoxColumn> negationColumn = new CheckBoxColumn>(getPageBase() .createStringResource("SearchPropertiesConfigPanel.table.column.applyNegotiation"), - "value." + SearchConfigDto.F_NEGATION); + "value." + ValueSearchFilterItem.F_APPLY_NEGATION); columns.add(negationColumn); return columns; } - private List getAvailablePropertiesList() { - PrismObjectDefinition objectDef = SearchFactory.findObjectDefinition(getObjectClass(), null, getPageBase()); - List availableDefs = SearchFactory.getAvailableDefinitions(objectDef, true); - List propertiesList = new ArrayList<>(); - availableDefs.forEach(searchItemDef -> propertiesList.add(new Property(searchItemDef.getDef()))); - return propertiesList; + private LoadableModel> getAvailablePropertiesListModel() { + return new LoadableModel>() { + @Override + protected List load() { + PrismObjectDefinition objectDef = SearchFactory.findObjectDefinition(getType(), null, getPageBase()); + List availableDefs = SearchFactory.getAvailableDefinitions(objectDef, true); + List propertiesList = new ArrayList<>(); + availableDefs.forEach(searchItemDef -> { + if (!isPropertyAlreadyAdded(searchItemDef.getDef())) { + propertiesList.add(new Property(searchItemDef.getDef())); + } + }); + return propertiesList; + } + }; + } + + private boolean isPropertyAlreadyAdded(ItemDefinition def){ + List> properties = provider.getAvailableData(); + for (SelectableBean prop : properties){ + if (QNameUtil.match(prop.getValue().getPropertyPath(), def.getItemName())){ + return true; + } + } + return false; } - private LoadableModel> getSearchConfigModel() { - return new LoadableModel>(true) { + private LoadableModel> getSearchFilterItemModel() { + return new LoadableModel>(true) { private static final long serialVersionUID = 1L; @Override - protected List load() { - List searchConfigDtos = new ArrayList<>(); - getModelObject().getItems().forEach(searchItem -> { - searchConfigDtos.add(SearchConfigDto.createSearchConfigDto(searchItem)); - }); - return searchConfigDtos; + protected List load() { + BasicSearchFilter basicSearchFilter = getModelObject(); + if (basicSearchFilter == null){ + return new ArrayList<>(); + } + return basicSearchFilter.getValueSearchFilterItems(); } }; } private void propertyAddedPerformed(AjaxRequestTarget target) { - Property newPropertyValue = propertyChoiceModel.getObject(); + Property newPropertyValue = getPropertyChoicePanel().getBaseFormComponent().getModelObject(); if (newPropertyValue != null) { - getModelObject().addItem(newPropertyValue.getDefinition()); - initTable((WebMarkupContainer) get(ID_CONFIGURATION_PANEL)); //todo don't re-init table! - target.add(get(createComponentPath(ID_CONFIGURATION_PANEL, ID_PROPERTIES_TABLE))); + getModelObject().addSearchFilterItem(createDefaultValueFilter(newPropertyValue)); } target.add(SearchPropertiesConfigPanel.this); } - private Property getSelectedProperty() { - DropDownChoicePanel propertyChoicePanel = (DropDownChoicePanel) get(getPageBase() - .createComponentPath(ID_CONFIGURATION_PANEL, ID_PROPERTY_CONFIG_CONTAINER, ID_PROPERTY_CHOICE)); - return propertyChoicePanel.getModel().getObject(); + private DropDownChoicePanel getPropertyChoicePanel(){ + return (DropDownChoicePanel) get(createComponentPath(ID_CONFIGURATION_PANEL, ID_PROPERTY_CONFIG_CONTAINER, ID_PROPERTY_CHOICE)); + } + + private ValueSearchFilterItem createDefaultValueFilter(Property property) { + if (property == null){ + return null; + } + return new ValueSearchFilterItem(property, false); + } + + private Component getPropertyValueField(String id, IModel> rowModel) { + Component searchItemField = null; + ValueSearchFilterItem valueSearchFilter = rowModel.getObject().getValue(); + ItemDefinition propertyDef = valueSearchFilter.getPropertyDef(); + if (propertyDef != null) { + PrismObject lookupTable = WebComponentUtil.findLookupTable(propertyDef, getPageBase()); + + if (propertyDef instanceof PrismReferenceDefinition) { + ObjectReferenceType propertyValue = (ObjectReferenceType) valueSearchFilter.getValue(); + searchItemField = new ReferenceValueSearchPanel(id, Model.of(propertyValue), + (PrismReferenceDefinition) propertyDef); + } else if (propertyDef instanceof PrismPropertyDefinition) { + List allowedValues = new ArrayList<>(); + if (((PrismPropertyDefinition) propertyDef).getAllowedValues() != null) { + allowedValues.addAll(((PrismPropertyDefinition) propertyDef).getAllowedValues()); + } + if (lookupTable != null) { + searchItemField = new AutoCompleteTextPanel(id, + new PropertyModel<>(rowModel, "value." + ValueSearchFilterItem.F_VALUE), String.class, + true, lookupTable.asObjectable()) { + + private static final long serialVersionUID = 1L; + + @Override + public Iterator getIterator(String input) { + return WebComponentUtil.prepareAutoCompleteList(lookupTable.asObjectable(), input, + ((PageBase) getPage()).getLocalizationService()).iterator(); + } + }; + } else if (CollectionUtils.isNotEmpty(allowedValues)) { + searchItemField = new DropDownChoicePanel(id, + new PropertyModel<>(rowModel, "value." + ValueSearchFilterItem.F_VALUE), + Model.ofList(allowedValues), new IChoiceRenderer() { + private static final long serialVersionUID = 1L; + + @Override + public Object getDisplayValue(DisplayableValue val) { + return val.getLabel(); + } + + @Override + public String getIdValue(DisplayableValue val, int index) { + return Integer.toString(index); + } + + @Override + public DisplayableValue getObject(String id, IModel> choices) { + return StringUtils.isNotBlank(id) ? choices.getObject().get(Integer.parseInt(id)) : null; + } + }, true); + } else { + searchItemField = new TextPanel(id, new PropertyModel<>(rowModel, "value." + ValueSearchFilterItem.F_VALUE)); + + } + } + } + if (searchItemField != null && searchItemField instanceof InputPanel){ + ((InputPanel) searchItemField).getBaseFormComponent().add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); + } + return searchItemField != null ? searchItemField : new WebMarkupContainer(id); + } + + @Override + protected void okButtonClicked(AjaxRequestTarget target){ + ObjectFilter configuredFilter = getModelObject().buildObjectFilter(); + filterConfiguredPerformed(configuredFilter, target); + } + + protected void filterConfiguredPerformed(ObjectFilter configuredFilter, AjaxRequestTarget target){ } public int getWidth() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/BasicSearchFilter.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/BasicSearchFilter.java new file mode 100644 index 00000000000..aaab97d87bc --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/BasicSearchFilter.java @@ -0,0 +1,107 @@ +/* + * 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.midpoint.web.component.search.filter; + +import java.util.Arrays; +import java.util.List; + +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.query.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; + +/** + * @author honchar + */ +public class BasicSearchFilter extends SearchFilter { + + private static final long serialVersionUID = 1L; + private static final Trace LOGGER = TraceManager.getTrace(BasicSearchFilter.class); + + private LogicalFilterValue logicalFilterValue = LogicalFilterValue.AND; + + public BasicSearchFilter(PrismContext prismContext, ObjectFilter baseFilter, Class type) { + super(prismContext, baseFilter, type); + } + + @Override + public void addSearchFilterItem(ValueSearchFilterItem valueSearchFilterItem) { + getValueSearchFilterItems().add(valueSearchFilterItem); + } + +// public ObjectFilter convertToObjectFilter() { +// if (CollectionUtils.isEmpty(getSearchFilterItems())) { +// return null; +// } +// List subFilterList = new ArrayList<>(); +// getSearchFilterItems().forEach(searchFilterItem -> { +// subFilterList.add(((ValueSearchFilterItem) searchFilterItem).buildObjectFilter()); +// }); +// if (logicalFilterItem == null && subFilterList.size() == 1) { +// return subFilterList.get(0); +// } +// if (logicalFilterItem == null) { +// logicalFilterItem = getPrismContext().queryFactory().createAnd(); +// } +// if (logicalFilterItem instanceof OrSearchFilterItem) { +// return getPrismContext().queryFactory().createOr(subFilterList); +// } else { +// return getPrismContext().queryFactory().createAnd(subFilterList); +// } +// } + + protected void initSearchFilterItems(ObjectFilter baseFilter) { + if (baseFilter == null) { + return; + } + if (baseFilter instanceof AndFilter) { + logicalFilterValue = LogicalFilterValue.AND; + + AndFilter andFilter = (AndFilter) baseFilter; + addValueFilters(andFilter.getConditions()); + } else if (baseFilter instanceof OrFilter) { + logicalFilterValue = LogicalFilterValue.OR; + + OrFilter orFilter = (OrFilter) baseFilter; + addValueFilters(orFilter.getConditions()); + } else if (baseFilter instanceof ValueFilter) { + addValueFilters(Arrays.asList(baseFilter)); + } + } + + @Override + public ObjectFilter buildObjectFilter(){ + if (logicalFilterValue.equals(LogicalFilterValue.OR)){ + return getPrismContext().queryFactory().createAnd(getObjectFilterList()); + } else { + return getPrismContext().queryFactory().createOr(getObjectFilterList()); + } + } + + public void addValueFilters(List objectFilters) { + objectFilters.forEach(filter -> { + boolean applyNegation = false; + if (filter instanceof NotFilter){ + applyNegation = true; + } + ObjectFilter realFilter = applyNegation ? ((NotFilter) filter).getFilter() : filter; + if (realFilter instanceof ValueFilter) { + ValueSearchFilterItem filterItem = new ValueSearchFilterItem((ValueFilter)realFilter, applyNegation); + addSearchFilterItem(filterItem); + } + }); + } + + public LogicalFilterValue getLogicalFilterValue() { + return logicalFilterValue; + } + + public void setLogicalFilterValue(LogicalFilterValue logicalFilterValue) { + this.logicalFilterValue = logicalFilterValue; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/SearchFilter.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/SearchFilter.java new file mode 100644 index 00000000000..c5d07970e63 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/SearchFilter.java @@ -0,0 +1,66 @@ +/* + * 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.midpoint.web.component.search.filter; + +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author honchar + */ +public abstract class SearchFilter implements Serializable { + + private static final long serialVersionUID = 1L; + private List valueSearchFilterItems = new ArrayList<>(); +// private LogicalSearchFilterItem logicalSearchFilterItem; + private PrismContext prismContext; + private ObjectFilter baseFilter; + Class type; + + public enum LogicalFilterValue{ + AND, + OR; + } + + public SearchFilter(PrismContext prismContext, ObjectFilter baseFilter, Class type){ + this.prismContext = prismContext; + this.baseFilter = baseFilter; + this.type = type; + + initSearchFilterItems(baseFilter); + } + + public List getValueSearchFilterItems() { + return valueSearchFilterItems; //todo return unmodifiable list + } + + public List getObjectFilterList() { + List objectFilters = new ArrayList<>(); + valueSearchFilterItems.forEach(filterItem -> objectFilters.add(filterItem.buildFilter(prismContext, type))); + return objectFilters; + } + + public abstract void addSearchFilterItem(ValueSearchFilterItem valueSearchFilterItem); + + protected abstract void initSearchFilterItems(ObjectFilter baseFilter); + + public abstract ObjectFilter buildObjectFilter(); + + public Class getType(){ + return type; + } + + public PrismContext getPrismContext() { + return prismContext; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/ValueSearchFilterItem.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/ValueSearchFilterItem.java new file mode 100644 index 00000000000..dbb6ccd753e --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/search/filter/ValueSearchFilterItem.java @@ -0,0 +1,278 @@ +/* + * 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.midpoint.web.component.search.filter; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.xml.namespace.QName; + +import org.apache.commons.collections.CollectionUtils; + +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.query.*; +import com.evolveum.midpoint.prism.query.builder.S_ConditionEntry; +import com.evolveum.midpoint.web.component.search.Property; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +/** + * @author honchar + */ +public class ValueSearchFilterItem implements Serializable { + + private static final long serialVersionUID = 1L; + public static final String F_VALUE = "value"; + public static final String F_FILTER_TYPE_NAME = "filterTypeName"; + public static final String F_APPLY_NEGATION = "applyNegation"; + public static final String F_FILTER = "filter"; + public static final String F_MATCHING_RULE = "matchingRule"; + public static final String F_PROPERTY_NAME = "propertyName"; + public static final String F_PROPERTY_PATH = "propertyPath"; + + public enum FilterName { + EQUAL(EqualFilter.class), + GREATER_OR_EQUAL(GreaterFilter.class), + GREATER(GreaterFilter.class), + LESS_OR_EQUAL(LessFilter.class), + LESS(LessFilter.class), + REF(RefFilter.class), + SUBSTRING(SubstringFilter.class), + SUBSTRING_ANCHOR_START(SubstringFilter.class), + SUBSTRING_ANCHOR_END(SubstringFilter.class); +// SUBSTRING_ANCHOR_START_AND_END("SUBSTRING_ANCHOR_START_AND_END"); //seems repeats usual substring + + private Class filterType; + + FilterName(Class filterType) { + this.filterType = filterType; + } + + public Class getFilterType() { + return filterType; + } + + public static FilterName findFilterName(F filter) { + if (filter instanceof LessFilter && ((LessFilter) filter).isEquals()) { + return FilterName.LESS_OR_EQUAL; + } else if (filter instanceof GreaterFilter && ((GreaterFilter) filter).isEquals()) { + return FilterName.GREATER_OR_EQUAL; + } else if (filter instanceof SubstringFilter && ((SubstringFilter) filter).isAnchorStart() && !((SubstringFilter) filter).isAnchorEnd()) { + return FilterName.SUBSTRING_ANCHOR_START; + } else if (filter instanceof SubstringFilter && ((SubstringFilter) filter).isAnchorEnd() && !((SubstringFilter) filter).isAnchorStart()) { + return FilterName.SUBSTRING_ANCHOR_END; + } else{ + return findFilterName(filter.getClass()); + } + } + + public static FilterName findFilterName(Class filterType){ + for (FilterName filterName : values()){ + if (filterName.getFilterType().equals(filterType.getInterfaces()[0])){ + return filterName; + } + } + return null; + } + } + + public enum MatchingRule { + STRING_IGNORE_CASE(PrismConstants.STRING_IGNORE_CASE_MATCHING_RULE_NAME), + POLY_STRING_STRICT(PrismConstants.POLY_STRING_STRICT_MATCHING_RULE_NAME), + POLY_STRING_ORIG(PrismConstants.POLY_STRING_ORIG_MATCHING_RULE_NAME), + POLY_STRING_NORM(PrismConstants.POLY_STRING_NORM_MATCHING_RULE_NAME), + EXCHANGE_EMAIL_ADDRESSES(PrismConstants.EXCHANGE_EMAIL_ADDRESSES_MATCHING_RULE_NAME), + DISTINGUISHED_NAME(PrismConstants.DISTINGUISHED_NAME_MATCHING_RULE_NAME), + XML(PrismConstants.XML_MATCHING_RULE_NAME), + UUID(PrismConstants.UUID_MATCHING_RULE_NAME), + DEFAULT(PrismConstants.DEFAULT_MATCHING_RULE_NAME); + + private QName matchingRuleName; + + MatchingRule(QName matchingRuleName) { + this.matchingRuleName = matchingRuleName; + } + + public QName getMatchingRuleName() { + return matchingRuleName; + } + } + + private boolean applyNegation; + private ValueFilter filter; + private FilterName filterTypeName = FilterName.EQUAL; + private MatchingRule matchingRule = null; + private String propertyName; + private QName propertyPath; + private Object value; + ItemDefinition propertyDef; + + public ValueSearchFilterItem(ValueFilter filter, boolean applyNegation) { + this.filter = filter; + this.applyNegation = applyNegation; + propertyName = filter.getElementName().toString(); + propertyPath = filter.getElementName(); + propertyDef = filter.getDefinition(); + value = CollectionUtils.isNotEmpty(filter.getValues()) ? filter.getValues().get(0) : null; + parseFilterName(); + } + + public ValueSearchFilterItem(Property property, boolean applyNegation) { + propertyName = property.getDefinition().getItemName().toString(); + propertyPath = property.getDefinition().getItemName(); + propertyDef = property.getDefinition(); + this.applyNegation = applyNegation; + if (propertyDef instanceof PrismReferenceDefinition){ + value = new ObjectReferenceType(); + } + parseFilterName(); + } + + public boolean isApplyNegation() { + return applyNegation; + } + + public void setApplyNegation(boolean applyNegation) { + this.applyNegation = applyNegation; + } + + public ValueFilter getFilter() { + return filter; + } + + public void setFilter(ValueFilter filter) { + this.filter = filter; + } + + //todo which filter types do we want to support here + public Object getValue() { + if (value instanceof PrismValue) { + return ((PrismValue) value).getRealValue(); + } + return null; + } + + public FilterName getFilterTypeName() { + return filterTypeName; + } + + public void setFilterTypeName(FilterName filterTypeName) { + this.filterTypeName = filterTypeName; + } + + public MatchingRule getMatchingRule() { + return matchingRule; + } + + public void setMatchingRule(MatchingRule matchingRule) { + this.matchingRule = matchingRule; + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + public QName getPropertyPath() { + return propertyPath; + } + + public void setPropertyPath(QName propertyPath) { + this.propertyPath = propertyPath; + } + + public void setValue(Object value) { + this.value = value; + } + + public ItemDefinition getPropertyDef() { + return propertyDef; + } + + public void setPropertyDef(ItemDefinition propertyDef) { + this.propertyDef = propertyDef; + } + + public ObjectFilter buildFilter(PrismContext prismContext, Class type){ + S_ConditionEntry conditionEntry = prismContext.queryFor(type).item(propertyPath); + ObjectFilter builtFilter = null; + if (FilterName.EQUAL.equals(filterTypeName)) { + builtFilter = conditionEntry.eq(value).buildFilter(); + } else if (FilterName.GREATER.equals(filterTypeName)) { + builtFilter = conditionEntry.gt(value).buildFilter(); + } else if (FilterName.GREATER_OR_EQUAL.equals(filterTypeName)) { + builtFilter = conditionEntry.ge(value).buildFilter(); + } else if (FilterName.LESS.equals(filterTypeName)) { + builtFilter = conditionEntry.lt(value).buildFilter(); + } else if (FilterName.LESS_OR_EQUAL.equals(filterTypeName)) { + builtFilter = conditionEntry.le(value).buildFilter(); + } else if (FilterName.REF.equals(filterTypeName) && value != null) { + PrismReferenceValue refVal = (PrismReferenceValue) value; + //todo do we need to separately create refType and refRelation ? +// if (StringUtils.isNotEmpty(refVal.getOid())){ +// +// } + if (refVal.getParent() instanceof RefFilter){ + builtFilter = (RefFilter) refVal.getParent(); + } else { + builtFilter = conditionEntry.ref(refVal).buildFilter(); + } + } else if (FilterName.SUBSTRING.equals(filterTypeName)) { + builtFilter = conditionEntry.contains(value).buildFilter(); + } else if (FilterName.SUBSTRING_ANCHOR_START.equals(filterTypeName)) { + builtFilter = conditionEntry.startsWith(value).buildFilter(); + } else if (FilterName.SUBSTRING_ANCHOR_END.equals(filterTypeName)) { + builtFilter = conditionEntry.endsWith(value).buildFilter(); + } + if (builtFilter instanceof ValueFilter && matchingRule != null){ + ((ValueFilter) builtFilter).setMatchingRule(matchingRule.getMatchingRuleName()); + } + if (isApplyNegation()){ + builtFilter = prismContext.queryFactory().createNot(builtFilter); + } + return builtFilter != null ? builtFilter : prismContext.queryFor(type).buildFilter(); + } + + private void parseFilterName (){ + if (propertyDef instanceof PrismReferenceDefinition){ + filterTypeName = FilterName.REF; + } else if (filter != null) { + filterTypeName = FilterName.findFilterName(filter); + } + } + + public List getAvailableFilterNameList(){ + if (propertyDef == null){ + return Arrays.asList(FilterName.values()); + } + if (propertyDef instanceof PrismReferenceDefinition){ + return Collections.singletonList(FilterName.REF); + } else { + List filterNames = new ArrayList<>(); + for (FilterName val : FilterName.values()) { + if (!FilterName.REF.equals(val)){ + filterNames.add(val); + } + } + return filterNames; + } + } + + public List getAvailableMatchingRuleList(){ + List matchingRules = Arrays.asList(MatchingRule.values()); + if (propertyDef == null){ + return matchingRules; + } + //todo + return matchingRules; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.html index 281057c5fc7..cdcb7e4dfee 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.html @@ -8,7 +8,9 @@ -

+
+

+

diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.java index 3e77c96b71b..054a91b0ce6 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/AceEditorPanel.java @@ -9,8 +9,10 @@ import com.evolveum.midpoint.gui.api.component.BasePanel; import com.evolveum.midpoint.web.component.AceEditor; +import com.evolveum.midpoint.web.component.util.VisibleBehaviour; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; +import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.model.IModel; @@ -20,6 +22,7 @@ public class AceEditorPanel extends BasePanel { private static final String ID_TITLE = "title"; + private static final String ID_TITLE_CONTAINER = "titleContainer"; private static final String ID_EDITOR = "editor"; private IModel title; @@ -40,14 +43,14 @@ public AceEditorPanel(String id, IModel title, IModel data, int private void initLayout(int minSize) { + WebMarkupContainer titleContainer = new WebMarkupContainer(ID_TITLE_CONTAINER); + titleContainer.setOutputMarkupId(true); + titleContainer.add(new VisibleBehaviour(() -> title != null)); + add(titleContainer); + Label title = new Label(ID_TITLE, this.title); - title.add(new VisibleEnableBehaviour() { - @Override - public boolean isVisible() { - return title != null; - } - }); - add(title); + title.setOutputMarkupId(true); + titleContainer.add(title); AceEditor editor = new AceEditor(ID_EDITOR, getModel()); editor.setReadonly(false); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/SearchFilterConfigurationPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/SearchFilterConfigurationPanel.html new file mode 100644 index 00000000000..ea492496de9 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/SearchFilterConfigurationPanel.html @@ -0,0 +1,19 @@ + + + + +
+ + + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/SearchFilterConfigurationPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/SearchFilterConfigurationPanel.java new file mode 100644 index 00000000000..6c7a714d250 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/reports/component/SearchFilterConfigurationPanel.java @@ -0,0 +1,98 @@ +/* + * 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.midpoint.web.page.admin.reports.component; + +import com.evolveum.midpoint.gui.api.component.BasePanel; +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.gui.impl.factory.panel.SearchFilterTypeModel; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.LoggingUtils; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.web.component.search.BasicSearchFilterModel; +import com.evolveum.midpoint.web.component.search.SearchPropertiesConfigPanel; +import com.evolveum.midpoint.web.component.search.filter.BasicSearchFilter; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.model.IModel; +import org.jetbrains.annotations.NotNull; + +import javax.xml.namespace.QName; + +/** + * @author honchar + */ +public class SearchFilterConfigurationPanel extends BasePanel { + private static final long serialVersionUID = 1L; + + private static final Trace LOGGER = TraceManager.getTrace(SearchFilterConfigurationPanel.class); + + private static final String ID_ACE_EDITOR_FIELD = "aceEditorField"; + private static final String ID_CONFIGURE_BUTTON = "configureButton"; + + Class filterType; + + public SearchFilterConfigurationPanel(String id, IModel model, Class filterType){ + super(id, model); + this.filterType = filterType; + } + + @Override + protected void onInitialize(){ + super.onInitialize(); + initLayout(); + } + + private void initLayout(){ + AceEditorPanel aceEditorField = new AceEditorPanel(ID_ACE_EDITOR_FIELD, null, new SearchFilterTypeModel(getModel(), getPageBase()), 10); + aceEditorField.setOutputMarkupId(true); + add(aceEditorField); + + AjaxLink searchConfigurationButton = new AjaxLink(ID_CONFIGURE_BUTTON) { + private static final long serialVersionUID = 1L; + + @Override + public void onClick(AjaxRequestTarget target) { + searchConfigurationPerformed(target); + } + }; + searchConfigurationButton.setOutputMarkupId(true); + add(searchConfigurationButton); + + } + + private void searchConfigurationPerformed(AjaxRequestTarget target){ + SearchPropertiesConfigPanel configPanel = new SearchPropertiesConfigPanel(getPageBase().getMainPopupBodyId(), + new BasicSearchFilterModel(getModel(), filterType, getPageBase()), filterType){ + private static final long serialVersionUID = 1L; + + @Override + protected void filterConfiguredPerformed(ObjectFilter configuredFilter, AjaxRequestTarget target){ + getPageBase().hideMainPopup(target); + + try { + if (configuredFilter == null) { + return; + } + SearchFilterConfigurationPanel.this.getModel().setObject(getPageBase().getPrismContext().getQueryConverter().createSearchFilterType(configuredFilter)); + target.add(getAceEditorPanel()); + } catch (SchemaException e) { + LoggingUtils.logUnexpectedException(LOGGER, "Cannot serialize filter", e); + } + } + }; + getPageBase().showMainPopup(configPanel, target); + } + + private AceEditorPanel getAceEditorPanel(){ + return (AceEditorPanel) get(ID_ACE_EDITOR_FIELD); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageSelfDashboard.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageSelfDashboard.java index 8da27ce5c9b..5fc5281532e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageSelfDashboard.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageSelfDashboard.java @@ -8,6 +8,7 @@ import static com.evolveum.midpoint.prism.PrismConstants.T_PARENT; import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType.F_CREATE_TIMESTAMP; +import static java.util.Collections.emptyList; import java.util.*; @@ -175,9 +176,8 @@ protected SecurityContextAwareCallable>> c private static final long serialVersionUID = 1L; @Override - public CallableResult> callWithContextPrepared() throws Exception { - setupContext(application, session); // TODO is this correct? [med] - return loadWorkItems(); + public CallableResult> callWithContextPrepared() { + return new CallableResult<>(emptyList(), null); // it is ignored anyway - FIXME } }; } @@ -227,9 +227,8 @@ protected SecurityContextAwareCallable>> createCal private static final long serialVersionUID = 1L; @Override - public CallableResult> callWithContextPrepared() throws Exception { - setupContext(application, session); - return loadMyRequests(); + public CallableResult> callWithContextPrepared() { + return new CallableResult<>(emptyList(), null); // it is ignored anyway - FIXME } }; } @@ -272,99 +271,6 @@ public boolean isVisible() { initAssignments(); } - private CallableResult> loadWorkItems() { - - LOGGER.debug("Loading work items."); - - AccountCallableResult callableResult = new AccountCallableResult(); - List list = new ArrayList<>(); - callableResult.setValue(list); - - if (!getWorkflowManager().isEnabled()) { - return callableResult; - } - - PrismObject user = principalModel.getObject(); - if (user == null) { - return callableResult; - } - - Task task = createSimpleTask(OPERATION_LOAD_WORK_ITEMS); - OperationResult result = task.getResult(); - callableResult.setResult(result); - - try { - // TODO try to use current state (user) instead of potentially obsolete principal - // but this requires some computation (of deputy relation) - // (Note that the current code is consistent with the other places where work items are displayed.) - S_FilterEntryOrEmpty q = getPrismContext().queryFor(CaseWorkItemType.class); - ObjectQuery query = QueryUtils.filterForAssignees(q, SecurityUtils.getPrincipalUser(), - OtherPrivilegesLimitationType.F_APPROVAL_WORK_ITEMS, getRelationRegistry()) - .desc(F_CREATE_TIMESTAMP) - .build(); - Collection> options = getOperationOptionsBuilder() - .item(T_PARENT, CaseType.F_OBJECT_REF).resolve() - .item(T_PARENT, CaseType.F_TARGET_REF).resolve() - .build(); - List workItems = getModelService().searchContainers(CaseWorkItemType.class, query, options, task, result); - callableResult.setValue(workItems); - } catch (Exception e) { - result.recordFatalError(getString("PageSelfDashboard.message.loadWorkItems.fatalError"), e); - } - - result.recordSuccessIfUnknown(); - result.recomputeStatus(); - - LOGGER.debug("Finished work items loading."); - - return callableResult; - } - - private CallableResult> loadMyRequests() { - - LOGGER.debug("Loading requests."); - - AccountCallableResult> callableResult = new AccountCallableResult<>(); - List list = new ArrayList<>(); - callableResult.setValue(list); - - if (!getWorkflowManager().isEnabled()) { - return callableResult; - } - - PrismObject focus = principalModel.getObject(); - if (focus == null) { - return callableResult; - } - - Task task = createSimpleTask(OPERATION_LOAD_REQUESTS); - OperationResult result = task.getResult(); - callableResult.setResult(result); - - try { - S_FilterEntryOrEmpty q = getPrismContext().queryFor(CaseType.class); - ObjectQuery query = QueryUtils.filterForMyRequests(q, focus.getOid()) - .build(); - Collection> options = getOperationOptionsBuilder() - .item(CaseType.F_OBJECT_REF).resolve() - .item(CaseType.F_TARGET_REF).resolve() - .build(); - List> cases = getModelService().searchObjects(CaseType.class, query, options, task, result); - cases.forEach(caseObj -> list.add(caseObj.asObjectable())); - callableResult.setValue(list); - } catch (Exception e) { - result.recordFatalError(getString("PageSelfDashboard.message.loadWorkItems.fatalError"), e); - } - - result.recordSuccessIfUnknown(); - result.recomputeStatus(); - - LOGGER.debug("Finished requests loading."); - - return callableResult; - } - - private PrismObject loadFocus() { MidPointPrincipal principal = SecurityUtils.getPrincipalUser(); Validate.notNull(principal, "No principal"); diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java index aa45b8e66fb..f46f94d9f17 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java @@ -25,6 +25,4 @@ public interface Hacks { @VisibleForTesting void parseProtectedType(ProtectedDataType protectedType, MapXNode xmap, PrismContext prismContext, ParsingContext pc) throws SchemaException; - - Element serializeSingleElementMapToElement(MapXNode filterClauseXNode) throws SchemaException; } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ParsingContext.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ParsingContext.java index a0b0c6ddb50..96602f50593 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ParsingContext.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/ParsingContext.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.prism; +import com.evolveum.midpoint.prism.marshaller.XNodeProcessorEvaluationMode; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; @@ -40,4 +41,6 @@ public interface ParsingContext extends Cloneable { ParsingContext strict(); ParsingContext compat(); + + XNodeProcessorEvaluationMode getEvaluationMode(); } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java index 85eedc0fa4c..e983720b900 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java @@ -111,7 +111,7 @@ public class PrismConstants { public static final QName SCHEMA_APP_INFO = new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"); public static final QName A_MAX_OCCURS = new QName(NS_ANNOTATION, "maxOccurs"); - public static final String MULTIPLICITY_UNBONUNDED = "unbounded"; + public static final String MULTIPLICITY_UNBOUNDED = "unbounded"; public static final QName A_NAMESPACE = new QName(NS_ANNOTATION, "namespace"); public static final String A_NAMESPACE_PREFIX = "prefix"; diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java index 8a9367cca23..ded5107b9e0 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismContext.java @@ -25,7 +25,6 @@ import com.evolveum.midpoint.prism.delta.builder.S_ItemEntry; import com.evolveum.midpoint.prism.marshaller.ParsingMigrator; import com.evolveum.midpoint.prism.metadata.ValueMetadataFactory; -import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; import com.evolveum.midpoint.prism.path.CanonicalItemPath; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.UniformItemPath; @@ -374,18 +373,6 @@ default ItemPath toPath(ItemPathType path) { @NotNull SchemaFactory schemaFactory(); - /** - * TEMPORARY. WILL BE REMOVED AFTER PROTOTYPING IS OVER. - */ - @Experimental - void setValueMetadataMockUpFactory(ValueMetadataMockUpFactory factory); - - /** - * TEMPORARY. WILL BE REMOVED AFTER PROTOTYPING IS OVER. - */ - @Experimental - ValueMetadataMockUpFactory getValueMetadataMockUpFactory(); - @Experimental void setValueMetadataFactory(ValueMetadataFactory factory); diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismObject.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismObject.java index 76f1475306b..e330fc8546b 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismObject.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismObject.java @@ -174,7 +174,4 @@ static PrismObject asPrismObject(Objectable o) { static T asObjectable(PrismObject object) { return object != null ? object.asObjectable() : null; } - - default void fixMockUpValueMetadata() { - } } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java index 6d4d234ab42..de51e36d556 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java @@ -108,7 +108,7 @@ public interface PrismSerializer { T serialize(@NotNull RootXNode xnode) throws SchemaException; @NotNull - T serializeObjects(@NotNull List> objects, QName aggregateElementName) throws SchemaException; + T serializeObjects(@NotNull List> objects) throws SchemaException; T serialize(JAXBElement value) throws SchemaException; T serializeRealValue(Object value) throws SchemaException; diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java index 3bb8843fa0a..740ccf7ba09 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismValue.java @@ -47,7 +47,7 @@ public interface PrismValue extends Visitable, PathVisitable, Serializable, Debu void setParent(Itemable parent); @Experimental - Optional valueMetadata() throws SchemaException; + Optional valueMetadata(); /** * Maybe it is better to expect empty value metadata if these are absent. @@ -55,7 +55,7 @@ public interface PrismValue extends Visitable, PathVisitable, Serializable, Debu */ @Experimental @NotNull - ValueMetadata getValueMetadata() throws SchemaException; + ValueMetadata getValueMetadata(); /** * Sets metadata for this value. @@ -63,18 +63,6 @@ public interface PrismValue extends Visitable, PathVisitable, Serializable, Debu @Experimental void setValueMetadata(ValueMetadata valueMetadata); - @Experimental - default void createLiveMetadata() { - } - - /** - * Converts mock-up metadata (if present) into materialized form. - * TEMPORARY. Remove after mocking up is gone. - */ - @Experimental - default void fixMockUpValueMetadata() { - } - @NotNull ItemPath getPath(); diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.java deleted file mode 100644 index 6b6b85fc56a..00000000000 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/metadata/ValueMetadataMockUpFactory.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.midpoint.prism.metadata; - -import com.evolveum.midpoint.prism.PrismValue; -import com.evolveum.midpoint.prism.ValueMetadata; -import com.evolveum.midpoint.util.annotation.Experimental; -import com.evolveum.midpoint.util.exception.SchemaException; - -import java.util.Optional; - -/** - * Provides mock up value metadata for given prism value. - */ -@Experimental -public interface ValueMetadataMockUpFactory { - - Optional createValueMetadata(PrismValue value) throws SchemaException; - -} diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java index 01e6873a60c..5df2f586d96 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/schema/SchemaRegistry.java @@ -127,6 +127,8 @@ T findItemDefinitionByFullPath(Class getValueMetadataDefinition(); + boolean hasImplicitTypeDefinition(@NotNull QName itemName, @NotNull QName typeName); ItemDefinition resolveGlobalItemDefinition(QName itemName, @Nullable ComplexTypeDefinition complexTypeDefinition); diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/ItemDeltaItem.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/ItemDeltaItem.java index a3b8513ba9d..df7c915c49d 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/ItemDeltaItem.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/ItemDeltaItem.java @@ -535,22 +535,4 @@ private void validate(ItemDelta delta) { throw new IllegalArgumentException("Attempt to set delta without definition"); } } - - // TEMPORARY - public void fixMockUpValueMetadata() { - fixMockUpValueMetadata(itemOld); - fixMockUpValueMetadata(itemNew); - fixMockUpValueMetadata(delta); - } - - // TEMPORARY - private void fixMockUpValueMetadata(Visitable visitable) { - if (visitable != null) { - visitable.accept(v -> { - if (v instanceof PrismValue) { - ((PrismValue) v).fixMockUpValueMetadata(); - } - }); - } - } } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismContextFactory.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismContextFactory.java index 75748da6929..c18094ae08a 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismContextFactory.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismContextFactory.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.prism.util; import java.io.FileNotFoundException; +import java.io.IOException; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.util.exception.SchemaException; @@ -21,6 +22,6 @@ public interface PrismContextFactory { /** * Returns UNINITIALIZED prism context. */ - PrismContext createPrismContext() throws SchemaException, FileNotFoundException; + PrismContext createPrismContext() throws SchemaException, IOException; } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismTestUtil.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismTestUtil.java index 6268a3d8107..6d85433d023 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismTestUtil.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismTestUtil.java @@ -23,7 +23,6 @@ import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; -import org.w3c.dom.Element; import org.xml.sax.SAXException; import javax.xml.bind.JAXBElement; @@ -74,7 +73,7 @@ public static void resetPrismContext() throws SchemaException, SAXException, IOE prismContext = createInitializedPrismContext(); } - public static PrismContext createPrismContext() throws SchemaException, FileNotFoundException { + public static PrismContext createPrismContext() throws SchemaException, IOException { if (prismContextFactory == null) { throw new IllegalStateException("Cannot create prism context, no prism factory is set"); } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MapXNode.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MapXNode.java index ecbce8855b7..13e12d3f8c0 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MapXNode.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MapXNode.java @@ -7,19 +7,18 @@ package com.evolveum.midpoint.prism.xnode; -import com.evolveum.midpoint.util.DebugDumpable; -import com.evolveum.midpoint.util.exception.SchemaException; -import org.jetbrains.annotations.NotNull; - -import javax.xml.namespace.QName; -import java.io.Serializable; import java.util.Map; import java.util.Set; +import javax.xml.namespace.QName; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.util.exception.SchemaException; /** * Note we cannot use "extends Map" here, because in that case we would have to declare XNodeImpl as map value parameter. */ -public interface MapXNode extends XNode, Serializable, DebugDumpable { +public interface MapXNode extends XNode, MetadataAware { boolean containsKey(QName key); diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MetadataAware.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MetadataAware.java new file mode 100644 index 00000000000..a4a9b2f7aca --- /dev/null +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/MetadataAware.java @@ -0,0 +1,18 @@ +/* + * 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.midpoint.prism.xnode; + +/** + * Some XNodes can hold value metadata. + */ +public interface MetadataAware { + + MapXNode getMetadataNode(); + + void setMetadataNode(MapXNode metadata); +} diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/PrimitiveXNode.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/PrimitiveXNode.java index b9656cf81cf..7363825d00a 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/PrimitiveXNode.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/PrimitiveXNode.java @@ -16,7 +16,7 @@ /** * */ -public interface PrimitiveXNode extends XNode { +public interface PrimitiveXNode extends XNode, MetadataAware { String getGuessedFormattedValue() throws SchemaException; diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/SchemaXNode.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/SchemaXNode.java index 7d2e6a3c9fb..94b522fa2a6 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/SchemaXNode.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/SchemaXNode.java @@ -10,5 +10,5 @@ /** * */ -public interface SchemaXNode { +public interface SchemaXNode extends MetadataAware { } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/XNode.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/XNode.java index 300a2066622..40731a27ff5 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/XNode.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/XNode.java @@ -32,4 +32,8 @@ public interface XNode extends DebugDumpable, Visitable, Cloneable, Seria XNode clone(); Integer getMaxOccurs(); + + default boolean hasMetadata() { + return this instanceof MetadataAware && ((MetadataAware) this).getMetadataNode() != null; + } } diff --git a/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java b/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java index 18e43d27969..f57d5d239d1 100644 --- a/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java +++ b/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java @@ -119,13 +119,6 @@ public RootXNode getFilterClauseAsRootXNode() throws SchemaException { return clause != null ? clause.getSingleSubEntryAsRoot("getFilterClauseAsRootXNode") : null; } - public Element getFilterClauseAsElement(@NotNull PrismContext prismContext) throws SchemaException { - if (filterClauseXNode == null) { - return null; - } - return prismContext.hacks().serializeSingleElementMapToElement(filterClauseXNode); - } - public static SearchFilterType createFromParsedXNode(XNode xnode, ParsingContext pc, PrismContext prismContext) throws SchemaException { SearchFilterType filter = new SearchFilterType(); filter.parseFromXNode(xnode, pc, prismContext); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java index 61212ad6e94..7c00274b25a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java @@ -10,12 +10,10 @@ import javax.xml.namespace.QName; import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Element; import com.evolveum.midpoint.prism.Hacks; import com.evolveum.midpoint.prism.ParsingContext; import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; import com.evolveum.midpoint.prism.impl.marshaller.XNodeProcessorUtil; import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; @@ -58,16 +56,6 @@ public void parseProtectedType(ProtectedDataType protectedType, MapXNode XNodeProcessorUtil.parseProtectedType(protectedType, (MapXNodeImpl) xmap, prismContext, pc); } - @Override - public Element serializeSingleElementMapToElement(MapXNode filterClauseXNode) throws SchemaException { - DomLexicalProcessor domParser = getDomParser(prismContext); - return domParser.serializeSingleElementMapToElement(filterClauseXNode); - } - - private static DomLexicalProcessor getDomParser(@NotNull PrismContext prismContext) { - return ((PrismContextImpl) prismContext).getParserDom(); - } - @Override public void setXNodeType(XNode node, QName explicitTypeName, boolean explicitTypeDeclaration) { ((XNodeImpl) node).setTypeQName(explicitTypeName); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java index 7a8a00e4586..56f484c0148 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContextImpl.java @@ -24,7 +24,6 @@ import com.evolveum.midpoint.prism.impl.lex.LexicalProcessorRegistry; import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; import com.evolveum.midpoint.prism.metadata.ValueMetadataFactory; -import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; import com.evolveum.midpoint.prism.path.*; import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer; import com.evolveum.midpoint.prism.impl.polystring.AlphanumericPolyStringNormalizer; @@ -90,9 +89,6 @@ public final class PrismContextImpl implements PrismContext { @NotNull private final ItemPathParser itemPathParser; @NotNull private final SchemaFactory schemaFactory; - @Experimental // temporary - private ValueMetadataMockUpFactory valueMetadataMockUpFactory; - @Experimental private ValueMetadataFactory valueMetadataFactory; @@ -670,16 +666,6 @@ public SchemaFactory schemaFactory() { return schemaFactory; } - @Override - public void setValueMetadataMockUpFactory(ValueMetadataMockUpFactory factory) { - this.valueMetadataMockUpFactory = factory; - } - - @Override - public ValueMetadataMockUpFactory getValueMetadataMockUpFactory() { - return valueMetadataMockUpFactory; - } - @Override public void setValueMetadataFactory(ValueMetadataFactory valueMetadataFactory) { this.valueMetadataFactory = valueMetadataFactory; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java index e0da17652bc..058cb8e860a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismObjectImpl.java @@ -467,13 +467,4 @@ public static List asObjectableList(@NotNull List o.asObjectable()) .collect(Collectors.toList()); } - - // TEMPORARY - public void fixMockUpValueMetadata() { - accept(v -> { - if (v instanceof PrismValue) { - ((PrismValue) v).fixMockUpValueMetadata(); - } - }); - } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java index 3558a9d85d8..6adb3fbcc1f 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismValueImpl.java @@ -11,10 +11,8 @@ import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy; import com.evolveum.midpoint.prism.impl.metadata.ValueMetadataAdapter; -import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.SchemaException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -386,29 +384,25 @@ public Collection getAllValues(ItemPath path) { } @Override - public Optional valueMetadata() throws SchemaException { + public Optional valueMetadata() { if (valueMetadata != null) { return Optional.of(valueMetadata); } else { - return createMockUpValueMetadata(); + return Optional.empty(); } } @Override @NotNull - public ValueMetadata getValueMetadata() throws SchemaException { - if (valueMetadata != null) { - return valueMetadata; - } else { - Optional mockup = createMockUpValueMetadata(); - if (mockup.isPresent()) { - return mockup.get(); - } else if (prismContext != null && prismContext.getValueMetadataFactory() != null) { - return prismContext.getValueMetadataFactory().createEmpty(); + public ValueMetadata getValueMetadata() { + if (valueMetadata == null) { + if (prismContext != null && prismContext.getValueMetadataFactory() != null) { + valueMetadata = prismContext.getValueMetadataFactory().createEmpty(); } else { - return ValueMetadataAdapter.holding(new PrismContainerValueImpl<>()); + valueMetadata = ValueMetadataAdapter.holding(new PrismContainerValueImpl<>()); } } + return valueMetadata; } @Override @@ -416,37 +410,6 @@ public void setValueMetadata(ValueMetadata valueMetadata) { this.valueMetadata = valueMetadata; } - @Override - @Experimental - public void createLiveMetadata() { - valueMetadata = Objects.requireNonNull(prismContext, "no prism context") - .getValueMetadataFactory() - .createEmpty(); - } - - private Optional createMockUpValueMetadata() throws SchemaException { - PrismContext prismContext = getPrismContext(); - if (prismContext != null) { - ValueMetadataMockUpFactory factory = prismContext.getValueMetadataMockUpFactory(); - if (factory != null) { - return factory.createValueMetadata(this); - } - } - return Optional.empty(); - } - - // TEMPORARY - @Override - public void fixMockUpValueMetadata() { - if (valueMetadata == null) { - try { - createMockUpValueMetadata().ifPresent(metadata -> valueMetadata = metadata.clone()); - } catch (SchemaException e) { - throw new IllegalStateException(e); - } - } - } - @Override protected void performFreeze() { if (valueMetadata != null) { diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerDomTarget.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerDomTarget.java index 45411728b1d..8530b30677b 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerDomTarget.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerDomTarget.java @@ -14,15 +14,11 @@ import org.jetbrains.annotations.Nullable; import org.w3c.dom.Element; -import javax.xml.namespace.QName; import java.util.List; -/** - * @author mederly - */ public class SerializerDomTarget extends SerializerTarget { - public SerializerDomTarget(@NotNull PrismContextImpl prismContext) { + SerializerDomTarget(@NotNull PrismContextImpl prismContext) { super(prismContext); } @@ -34,8 +30,8 @@ public Element write(@NotNull RootXNodeImpl xroot, SerializationContext context) @NotNull @Override - public Element write(@NotNull List roots, @Nullable QName aggregateElementName, @Nullable SerializationContext context) + public Element write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException { - return prismContext.getLexicalProcessorRegistry().domProcessor().writeXRootListToElement(roots, aggregateElementName); + return prismContext.getLexicalProcessorRegistry().domProcessor().writeXRootListToElement(roots); } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerStringTarget.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerStringTarget.java index 797edf039e6..e140205d51b 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerStringTarget.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerStringTarget.java @@ -14,7 +14,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.xml.namespace.QName; import java.util.List; /** @@ -24,7 +23,7 @@ public class SerializerStringTarget extends SerializerTarget { @NotNull private final String language; - public SerializerStringTarget(@NotNull PrismContextImpl prismContext, @NotNull String language) { + SerializerStringTarget(@NotNull PrismContextImpl prismContext, @NotNull String language) { super(prismContext); this.language = language; } @@ -38,9 +37,9 @@ public String write(@NotNull RootXNodeImpl xroot, SerializationContext context) @NotNull @Override - public String write(@NotNull List roots, @Nullable QName aggregateElementName, @Nullable SerializationContext context) + public String write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException { LexicalProcessor lexicalProcessor = prismContext.getLexicalProcessorRegistry().processorFor(language); - return lexicalProcessor.write(roots, aggregateElementName, context); + return lexicalProcessor.write(roots, context); } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerTarget.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerTarget.java index d0e53bfb55e..c9c826a8cb4 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerTarget.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerTarget.java @@ -13,12 +13,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.xml.namespace.QName; import java.util.List; -/** - * @author mederly - */ public abstract class SerializerTarget { @NotNull public final PrismContextImpl prismContext; @@ -31,6 +27,5 @@ protected SerializerTarget(@NotNull PrismContextImpl prismContext) { abstract public T write(@NotNull RootXNodeImpl xroot, SerializationContext context) throws SchemaException; @NotNull - abstract public T write(@NotNull List roots, @Nullable QName aggregateElementName, - @Nullable SerializationContext context) throws SchemaException; + abstract public T write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerXNodeTarget.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerXNodeTarget.java index ea2259832f7..5a4fd03dc62 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerXNodeTarget.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/SerializerXNodeTarget.java @@ -13,12 +13,8 @@ import com.evolveum.midpoint.util.exception.SchemaException; import org.jetbrains.annotations.NotNull; -import javax.xml.namespace.QName; import java.util.List; -/** - * @author mederly - */ public class SerializerXNodeTarget extends SerializerTarget { public SerializerXNodeTarget(@NotNull PrismContextImpl prismContext) { @@ -33,7 +29,7 @@ public RootXNodeImpl write(@NotNull RootXNodeImpl xroot, SerializationContext co @NotNull @Override - public RootXNodeImpl write(@NotNull List roots, QName aggregateElementName, SerializationContext context) + public RootXNodeImpl write(@NotNull List roots, SerializationContext context) throws SchemaException { throw new UnsupportedOperationException("Serialization of a collection of objects is not supported for XNode target."); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessor.java index 38580c1df31..614d71d740a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessor.java @@ -85,5 +85,5 @@ interface RootXNodeHandler { * Not supported for NullLexicalProcessor, though. */ @NotNull - T write(@NotNull List roots, @Nullable QName aggregateElementName, @Nullable SerializationContext context) throws SchemaException; + T write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessorRegistry.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessorRegistry.java index 4eb82849670..31c543e63e5 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessorRegistry.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/LexicalProcessorRegistry.java @@ -14,6 +14,12 @@ import java.util.HashMap; import java.util.Map; +import com.evolveum.midpoint.prism.impl.lex.json.*; + +import com.evolveum.midpoint.prism.impl.lex.json.reader.JsonReader; +import com.evolveum.midpoint.prism.impl.lex.json.reader.YamlReader; +import com.evolveum.midpoint.prism.impl.lex.json.writer.JsonWriter; +import com.evolveum.midpoint.prism.impl.lex.json.writer.YamlWriter; import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.ParserFileSource; @@ -22,16 +28,10 @@ import com.evolveum.midpoint.prism.ParserXNodeSource; import com.evolveum.midpoint.prism.impl.ParserElementSource; import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; -import com.evolveum.midpoint.prism.impl.lex.json.JsonLexicalProcessor; -import com.evolveum.midpoint.prism.impl.lex.json.NullLexicalProcessor; -import com.evolveum.midpoint.prism.impl.lex.json.YamlLexicalProcessor; import com.evolveum.midpoint.prism.schema.SchemaRegistry; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SystemException; -/** - * @author mederly - */ public class LexicalProcessorRegistry { private final Map> parserMap; @@ -45,8 +45,8 @@ public LexicalProcessorRegistry(@NotNull SchemaRegistry schemaRegistry) { parserMap = new HashMap<>(); parserMap.put(LANG_XML, domLexicalProcessor); - parserMap.put(LANG_JSON, new JsonLexicalProcessor(schemaRegistry)); - parserMap.put(LANG_YAML, new YamlLexicalProcessor(schemaRegistry)); + parserMap.put(LANG_JSON, new DelegatingLexicalProcessor(new JsonReader(schemaRegistry), new JsonWriter())); + parserMap.put(LANG_YAML, new DelegatingLexicalProcessor(new YamlReader(schemaRegistry), new YamlWriter())); } @NotNull diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomIterativeReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomIterativeReader.java new file mode 100644 index 00000000000..aba8de12897 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomIterativeReader.java @@ -0,0 +1,111 @@ +/* + * 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.midpoint.prism.impl.lex.dom; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import com.evolveum.midpoint.prism.schema.SchemaRegistry; + +import org.apache.commons.io.IOUtils; +import org.codehaus.staxmate.dom.DOMConverter; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.evolveum.midpoint.prism.ParserSource; +import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; +import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; + +/** + * + */ +class DomIterativeReader { + + private final ParserSource source; + private final LexicalProcessor.RootXNodeHandler handler; + private final SchemaRegistry schemaRegistry; + + DomIterativeReader(ParserSource source, LexicalProcessor.RootXNodeHandler handler, SchemaRegistry schemaRegistry) { + this.source = source; + this.handler = handler; + this.schemaRegistry = schemaRegistry; + } + + // code taken from Validator class + public void readObjectsIteratively() throws SchemaException, IOException { + InputStream is = source.getInputStream(); + XMLStreamReader stream = null; + try { + stream = getXMLInputFactory().createXMLStreamReader(is); + + int eventType = stream.nextTag(); + if (eventType != XMLStreamConstants.START_ELEMENT) { + throw new SystemException("StAX Malfunction?"); + } + DOMConverter domConverter = new DOMConverter(); + Map rootNamespaceDeclarations = new HashMap<>(); + + QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); + if (objectsMarker != null && !QNameUtil.match(stream.getName(), objectsMarker)) { + readSingleObjectIteratively(stream, rootNamespaceDeclarations, domConverter, handler); + } + for (int i = 0; i < stream.getNamespaceCount(); i++) { + rootNamespaceDeclarations.put(stream.getNamespacePrefix(i), stream.getNamespaceURI(i)); + } + while (stream.hasNext()) { + eventType = stream.next(); + if (eventType == XMLStreamConstants.START_ELEMENT) { + if (!readSingleObjectIteratively(stream, rootNamespaceDeclarations, domConverter, handler)) { + return; + } + } + } + } catch (XMLStreamException ex) { + String lineInfo = stream != null + ? " on line " + stream.getLocation().getLineNumber() + : ""; + throw new SchemaException( + "Exception while parsing XML" + lineInfo + ": " + ex.getMessage(), ex); + } finally { + if (source.closeStreamAfterParsing()) { + IOUtils.closeQuietly(is); + } + } + + } + + private boolean readSingleObjectIteratively( + XMLStreamReader stream, Map rootNamespaceDeclarations, + DOMConverter domConverter, LexicalProcessor.RootXNodeHandler handler) + throws XMLStreamException, SchemaException { + Document objectDoc = domConverter.buildDocument(stream); + Element objectElement = DOMUtil.getFirstChildElement(objectDoc); + DOMUtil.setNamespaceDeclarations(objectElement, rootNamespaceDeclarations); + RootXNodeImpl rootNode = new DomReader(objectElement, schemaRegistry).read(); + return handler.handleData(rootNode); + } + + private XMLInputFactory getXMLInputFactory() { + XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false); + // TODO: cache? static? prism context? + return xmlInputFactory; + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java index be6c25d45c5..4248b698a58 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java @@ -10,22 +10,14 @@ import java.io.IOException; import java.io.InputStream; import java.util.*; -import java.util.Map.Entry; import java.util.regex.Pattern; import javax.xml.namespace.QName; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; + +import com.evolveum.midpoint.prism.marshaller.XNodeProcessorEvaluationMode; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.Validate; -import org.apache.commons.lang3.StringUtils; -import org.codehaus.staxmate.dom.DOMConverter; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -34,15 +26,12 @@ import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; import com.evolveum.midpoint.prism.impl.lex.LexicalUtils; import com.evolveum.midpoint.prism.impl.xnode.*; -import com.evolveum.midpoint.prism.marshaller.XNodeProcessorEvaluationMode; import com.evolveum.midpoint.prism.schema.SchemaRegistry; import com.evolveum.midpoint.prism.xnode.MapXNode; import com.evolveum.midpoint.prism.xnode.RootXNode; import com.evolveum.midpoint.prism.xnode.XNode; import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -50,8 +39,6 @@ public class DomLexicalProcessor implements LexicalProcessor { public static final Trace LOGGER = TraceManager.getTrace(DomLexicalProcessor.class); - private static final QName SCHEMA_ELEMENT_QNAME = DOMUtil.XSD_SCHEMA_ELEMENT; - @NotNull private final SchemaRegistry schemaRegistry; public DomLexicalProcessor(@NotNull SchemaRegistry schemaRegistry) { @@ -62,16 +49,17 @@ public DomLexicalProcessor(@NotNull SchemaRegistry schemaRegistry) { @Override public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { if (source instanceof ParserElementSource) { - return read(((ParserElementSource) source).getElement()); - } - - InputStream is = source.getInputStream(); - try { - Document document = DOMUtil.parse(is); - return read(document); - } finally { - if (source.closeStreamAfterParsing()) { - IOUtils.closeQuietly(is); + Element root = ((ParserElementSource) source).getElement(); + return new DomReader(root, schemaRegistry).read(); + } else { + InputStream is = source.getInputStream(); + try { + Document document = DOMUtil.parse(is); + return new DomReader(document, schemaRegistry).read(); + } finally { + if (source.closeStreamAfterParsing()) { + IOUtils.closeQuietly(is); + } } } } @@ -82,7 +70,7 @@ public List readObjects(@NotNull ParserSource source, @NotNull Pa InputStream is = source.getInputStream(); try { Document document = DOMUtil.parse(is); - return readObjects(document); + return new DomReader(document, schemaRegistry).readObjects(); } finally { if (source.closeStreamAfterParsing()) { IOUtils.closeQuietly(is); @@ -90,327 +78,14 @@ public List readObjects(@NotNull ParserSource source, @NotNull Pa } } - private XMLInputFactory getXMLInputFactory() { - XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); - xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); - xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false); - // TODO: cache? static? prism context? - return xmlInputFactory; - } - - // code taken from Validator class @Override public void readObjectsIteratively(@NotNull ParserSource source, @NotNull ParsingContext parsingContext, RootXNodeHandler handler) throws SchemaException, IOException { - - InputStream is = source.getInputStream(); - XMLStreamReader stream = null; - try { - stream = getXMLInputFactory().createXMLStreamReader(is); - - int eventType = stream.nextTag(); - if (eventType != XMLStreamConstants.START_ELEMENT) { - throw new SystemException("StAX Malfunction?"); - } - DOMConverter domConverter = new DOMConverter(); - Map rootNamespaceDeclarations = new HashMap<>(); - - QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); - if (objectsMarker != null && !QNameUtil.match(stream.getName(), objectsMarker)) { - readSingleObjectIteratively(stream, rootNamespaceDeclarations, domConverter, handler); - } - for (int i = 0; i < stream.getNamespaceCount(); i++) { - rootNamespaceDeclarations.put(stream.getNamespacePrefix(i), stream.getNamespaceURI(i)); - } - while (stream.hasNext()) { - eventType = stream.next(); - if (eventType == XMLStreamConstants.START_ELEMENT) { - if (!readSingleObjectIteratively(stream, rootNamespaceDeclarations, domConverter, handler)) { - return; - } - } - } - } catch (XMLStreamException ex) { - String lineInfo = stream != null - ? " on line " + stream.getLocation().getLineNumber() - : ""; - throw new SchemaException( - "Exception while parsing XML" + lineInfo + ": " + ex.getMessage(), ex); - } finally { - if (source.closeStreamAfterParsing()) { - IOUtils.closeQuietly(is); - } - } - } - - private boolean readSingleObjectIteratively( - XMLStreamReader stream, Map rootNamespaceDeclarations, - DOMConverter domConverter, RootXNodeHandler handler) - throws XMLStreamException, SchemaException { - Document objectDoc = domConverter.buildDocument(stream); - Element objectElement = DOMUtil.getFirstChildElement(objectDoc); - DOMUtil.setNamespaceDeclarations(objectElement, rootNamespaceDeclarations); - RootXNodeImpl rootNode = read(objectElement); - return handler.handleData(rootNode); - } - - private List readObjects(Document document) throws SchemaException { - Element root = DOMUtil.getFirstChildElement(document); - QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); - if (objectsMarker != null && !QNameUtil.match(DOMUtil.getQName(root), objectsMarker)) { - return Collections.singletonList(read(root)); - } else { - List rv = new ArrayList<>(); - for (Element child : DOMUtil.listChildElements(root)) { - rv.add(read(child)); - } - return rv; - } - } - - @NotNull - public RootXNodeImpl read(Document document) throws SchemaException { - Element rootElement = DOMUtil.getFirstChildElement(document); - return read(rootElement); - } - - @NotNull - public RootXNodeImpl read(Element rootElement) throws SchemaException { - QName rootElementName = DOMUtil.getQName(rootElement); - QName rootElementXsiType = DOMUtil.resolveXsiType(rootElement); - - RootXNodeImpl xroot = new RootXNodeImpl(rootElementName); - extractCommonMetadata(rootElement, rootElementXsiType, xroot); - XNodeImpl xnode = parseElementContent(rootElement, rootElementName, false); - xroot.setSubnode(xnode); - return xroot; - } - - private void extractCommonMetadata(Element element, QName xsiType, XNodeImpl xnode) - throws SchemaException { - - if (xsiType != null) { - xnode.setTypeQName(xsiType); - xnode.setExplicitTypeDeclaration(true); - } - - String maxOccursString = element.getAttributeNS( - PrismConstants.A_MAX_OCCURS.getNamespaceURI(), - PrismConstants.A_MAX_OCCURS.getLocalPart()); - if (!StringUtils.isBlank(maxOccursString)) { - int maxOccurs = parseMultiplicity(maxOccursString, element); - xnode.setMaxOccurs(maxOccurs); - } - } - - private int parseMultiplicity(String maxOccursString, Element element) throws SchemaException { - if (PrismConstants.MULTIPLICITY_UNBONUNDED.equals(maxOccursString)) { - return -1; - } - if (maxOccursString.startsWith("-")) { - return -1; - } - if (StringUtils.isNumeric(maxOccursString)) { - return Integer.parseInt(maxOccursString); - } else { - throw new SchemaException("Expected numeric value for " + PrismConstants.A_MAX_OCCURS.getLocalPart() - + " attribute on " + DOMUtil.getQName(element) + " but got " + maxOccursString); - } - } - - /** - * Parses the content of the element. - * - * @param knownElementName Pre-fetched element name. Might be null (this is expected if storeElementName is true). - */ - @NotNull - private XNodeImpl parseElementContent(@NotNull Element element, QName knownElementName, boolean storeElementName) throws SchemaException { - XNodeImpl node; - - QName xsiType = DOMUtil.resolveXsiType(element); - QName elementName = knownElementName != null ? knownElementName : DOMUtil.getQName(element); - - if (DOMUtil.hasChildElements(element) || DOMUtil.hasApplicationAttributes(element)) { - if (isList(element, elementName, xsiType)) { - node = parseElementContentToList(element); - } else { - node = parseElementContentToMap(element); - } - } else if (DOMUtil.isMarkedAsIncomplete(element)) { - // Note that it is of no use to check for "incomplete" on non-leaf elements. In XML the incomplete attribute - // must be attached to an empty element. - node = new IncompleteMarkerXNodeImpl(); - } else { - node = parsePrimitiveElement(element); - } - if (storeElementName) { - node.setElementName(elementName); - } - extractCommonMetadata(element, xsiType, node); - return node; - } - - // all the sub-elements should be compatible (this is not enforced here, however) - private ListXNodeImpl parseElementContentToList(Element element) throws SchemaException { - if (DOMUtil.hasApplicationAttributes(element)) { - throw new SchemaException("List should have no application attributes: " + element); - } - return parseElementList(DOMUtil.listChildElements(element), null, true); - } - - private MapXNodeImpl parseElementContentToMap(Element element) throws SchemaException { - MapXNodeImpl xmap = new MapXNodeImpl(); - - // Attributes - for (Attr attr : DOMUtil.listApplicationAttributes(element)) { - QName attrQName = DOMUtil.getQName(attr); - XNodeImpl subnode = parseAttributeValue(attr); - xmap.put(attrQName, subnode); - } - - // Sub-elements - QName lastElementName = null; - List lastElements = null; - for (Element childElement : DOMUtil.listChildElements(element)) { - QName childName = DOMUtil.getQName(childElement); - if (!match(childName, lastElementName)) { - parseSubElementsGroupAsMapEntry(xmap, lastElementName, lastElements); - lastElementName = childName; - lastElements = new ArrayList<>(); - } - lastElements.add(childElement); - } - parseSubElementsGroupAsMapEntry(xmap, lastElementName, lastElements); - return xmap; - } - - private boolean isList(@NotNull Element element, @NotNull QName elementName, @Nullable QName xsiType) { - String isListAttribute = DOMUtil.getAttribute(element, DOMUtil.IS_LIST_ATTRIBUTE_NAME); - if (StringUtils.isNotEmpty(isListAttribute)) { - return Boolean.parseBoolean(isListAttribute); - } - // enable this after schema registry is optional (now it's mandatory) -// if (schemaRegistry == null) { -// return false; -// } - - SchemaRegistry.IsList fromSchema = schemaRegistry.isList(xsiType, elementName); - if (fromSchema != SchemaRegistry.IsList.MAYBE) { - return fromSchema == SchemaRegistry.IsList.YES; - } - - // checking the content - if (DOMUtil.hasApplicationAttributes(element)) { - return false; // TODO - or should we fail in this case? - } - //System.out.println("Elements are compatible: " + DOMUtil.listChildElements(element) + ": " + rv); - return elementsAreCompatible(DOMUtil.listChildElements(element)); - } - - private boolean elementsAreCompatible(List elements) { - QName unified = null; - for (Element element : elements) { - QName root = getHierarchyRoot(DOMUtil.getQName(element)); - if (unified == null) { - unified = root; - } else if (!QNameUtil.match(unified, root)) { - return false; - } else if (QNameUtil.noNamespace(unified) && QNameUtil.hasNamespace(root)) { - unified = root; - } - } - return true; - } - - private QName getHierarchyRoot(QName name) { - ItemDefinition def = schemaRegistry.findItemDefinitionByElementName(name); - if (def == null || !def.isHeterogeneousListItem()) { - return name; - } else { - return def.getSubstitutionHead(); - } - } - - private boolean match(QName name, QName existing) { - if (existing == null) { - return false; - } else { - return QNameUtil.match(name, existing); - } - } - - // All elements share the same elementName - private void parseSubElementsGroupAsMapEntry(MapXNodeImpl xmap, QName elementName, List elements) throws SchemaException { - if (elements == null || elements.isEmpty()) { - return; - } - XNodeImpl xsub; - // We really want to have equals here, not match - // we want to be very explicit about namespace here - if (elementName.equals(SCHEMA_ELEMENT_QNAME)) { - if (elements.size() == 1) { - xsub = parseSchemaElement(elements.iterator().next()); - } else { - throw new SchemaException("Too many schema elements"); - } - } else if (elements.size() == 1) { - xsub = parseElementContent(elements.get(0), elementName, false); - } else { - xsub = parseElementList(elements, elementName, false); - } - xmap.merge(elementName, xsub); - } - - /** - * Parses elements that should form the list. - *

- * Either they have the same element name, or they are stored as a sub-elements of "list" parent element. - */ - @NotNull - @Contract("!null, null, false -> fail") - private ListXNodeImpl parseElementList(List elements, QName elementName, boolean storeElementNames) throws SchemaException { - if (!storeElementNames && elementName == null) { - throw new IllegalArgumentException("When !storeElementNames the element name must be specified"); - } - ListXNodeImpl xlist = new ListXNodeImpl(); - for (Element element : elements) { - xlist.add(parseElementContent(element, elementName, storeElementNames)); - } - return xlist; - } - - // @pre element has no children nor application attributes - private PrimitiveXNodeImpl parsePrimitiveElement(@NotNull Element element) { - PrimitiveXNodeImpl xnode = new PrimitiveXNodeImpl<>(); - xnode.setValueParser(new ElementValueParser<>(element)); - return xnode; - } - - static T processIllegalArgumentException(String value, QName typeName, IllegalArgumentException e, - XNodeProcessorEvaluationMode mode) { - if (mode == XNodeProcessorEvaluationMode.COMPAT) { - LOGGER.warn("Value of '{}' couldn't be parsed as '{}' -- interpreting as null because of COMPAT mode set", value, - typeName, e); - return null; - } else { - throw e; - } - } - - private PrimitiveXNodeImpl parseAttributeValue(@NotNull Attr attr) { - PrimitiveXNodeImpl xnode = new PrimitiveXNodeImpl<>(); - xnode.setValueParser(new AttributeValueParser<>(attr)); - xnode.setAttribute(true); - return xnode; + new DomIterativeReader(source, handler, schemaRegistry) + .readObjectsIteratively(); } - @NotNull - private SchemaXNodeImpl parseSchemaElement(Element schemaElement) { - SchemaXNodeImpl xschema = new SchemaXNodeImpl(); - xschema.setSchemaElement(schemaElement); - return xschema; - } @Override public boolean canRead(@NotNull File file) { @@ -428,80 +103,60 @@ public boolean canRead(@NotNull String dataString) { @NotNull @Override public String write(@NotNull XNode xnode, @NotNull QName rootElementName, SerializationContext serializationContext) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, serializationContext); RootXNodeImpl xroot = LexicalUtils.createRootXNode((XNodeImpl) xnode, rootElementName); - Element element = serializer.serialize(xroot); + Element element = + new DomWriter(schemaRegistry, serializationContext) + .writeRoot(xroot); return DOMUtil.serializeDOMToString(element); } @NotNull @Override public String write(@NotNull RootXNode xnode, SerializationContext serializationContext) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, serializationContext); - Element element = serializer.serialize((RootXNodeImpl) xnode); + Element element = + new DomWriter(schemaRegistry, serializationContext) + .writeRoot(xnode); return DOMUtil.serializeDOMToString(element); } @NotNull @Override - public String write(@NotNull List roots, @Nullable QName aggregateElementName, - @Nullable SerializationContext context) throws SchemaException { - Element aggregateElement = writeXRootListToElement(roots, aggregateElementName); - return DOMUtil.serializeDOMToString(aggregateElement); + public String write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException { + Element element = writeXRootListToElement(roots); + return DOMUtil.serializeDOMToString(element); } @NotNull - public Element writeXRootListToElement(@NotNull List roots, QName aggregateElementName) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); - return serializer.serialize(roots, aggregateElementName); + public Element writeXRootListToElement(@NotNull List roots) throws SchemaException { + return new DomWriter(schemaRegistry, null) + .writeRoots(roots); } - public Element serializeXMapToElement(MapXNodeImpl xmap, QName elementName) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); - return serializer.serializeToElement(xmap, elementName); - } - - private Element serializeXPrimitiveToElement(PrimitiveXNodeImpl xprim, QName elementName) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); - return serializer.serializeXPrimitiveToElement(xprim, elementName); + /** + * Seems to be used in strange circumstances (called from various hacks). + * To be reconsidered eventually. Avoid using in new code. + */ + @Deprecated + public Element writeXMapToElement(MapXNodeImpl xmap, QName elementName) throws SchemaException { + return new DomWriter(schemaRegistry, null) + .writeMap(xmap, elementName); } @NotNull public Element writeXRootToElement(@NotNull RootXNodeImpl xroot) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); - return serializer.serialize(xroot); - } - - private Element serializeToElement(XNodeImpl xnode, QName elementName) throws SchemaException { - Validate.notNull(xnode); - Validate.notNull(elementName); - if (xnode instanceof MapXNodeImpl) { - return serializeXMapToElement((MapXNodeImpl) xnode, elementName); - } else if (xnode instanceof PrimitiveXNodeImpl) { - return serializeXPrimitiveToElement((PrimitiveXNodeImpl) xnode, elementName); - } else if (xnode instanceof RootXNodeImpl) { - return writeXRootToElement((RootXNodeImpl) xnode); - } else if (xnode instanceof ListXNodeImpl) { - ListXNodeImpl xlist = (ListXNodeImpl) xnode; - if (xlist.size() == 0) { - return null; - } else if (xlist.size() > 1) { - throw new IllegalArgumentException("Cannot serialize list xnode with more than one item: " + xlist); - } else { - return serializeToElement(xlist.get(0), elementName); - } - } else { - throw new IllegalArgumentException("Cannot serialize " + xnode + " to element"); - } + return new DomWriter(schemaRegistry, null) + .writeRoot(xroot); } - public Element serializeSingleElementMapToElement(MapXNode map) throws SchemaException { - MapXNodeImpl xmap = (MapXNodeImpl) map; - if (xmap == null || xmap.isEmpty()) { + // TODO move somewhere + static T processIllegalArgumentException(String value, QName typeName, IllegalArgumentException e, + XNodeProcessorEvaluationMode mode) { + if (mode == XNodeProcessorEvaluationMode.COMPAT) { + LOGGER.warn("Value of '{}' couldn't be parsed as '{}' -- interpreting as null because of COMPAT mode set", value, + typeName, e); return null; + } else { + throw e; } - Entry subEntry = xmap.getSingleSubEntry(xmap.toString()); - Element parent = serializeToElement(xmap, subEntry.getKey()); - return DOMUtil.getFirstChildElement(parent); } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java deleted file mode 100644 index 204534e804a..00000000000 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * 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.prism.impl.lex.dom; - -import com.evolveum.midpoint.prism.SerializationContext; -import com.evolveum.midpoint.prism.SerializationOptions; -import com.evolveum.midpoint.prism.impl.PrismContextImpl; -import com.evolveum.midpoint.prism.impl.marshaller.ItemPathHolder; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.prism.xml.DynamicNamespacePrefixMapper; -import com.evolveum.midpoint.prism.xml.XsdTypeMapper; -import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.SchemaXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; -import com.evolveum.midpoint.prism.xnode.IncompleteMarkerXNode; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import org.apache.commons.lang.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; - -import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; - -/** - * @author semancik - * - */ -public class DomLexicalWriter { - - private Document doc; - private boolean serializeCompositeObjects = false; - private final SchemaRegistry schemaRegistry; - private final SerializationOptions serializationOptions; - - DomLexicalWriter(SchemaRegistry schemaRegistry, SerializationContext context) { - super(); - this.schemaRegistry = schemaRegistry; - this.serializationOptions = context != null ? context.getOptions() : null; - } - - public boolean isSerializeCompositeObjects() { - return serializeCompositeObjects; - } - - public void setSerializeCompositeObjects(boolean serializeCompositeObjects) { - this.serializeCompositeObjects = serializeCompositeObjects; - } - - private DynamicNamespacePrefixMapper getNamespacePrefixMapper() { - if (schemaRegistry == null) { - return null; - } - return schemaRegistry.getNamespacePrefixMapper(); - } - - private void initialize() { - doc = DOMUtil.getDocument(); - } - - private void initializeWithExistingDocument(Document document) { - doc = document; - document.getDocumentElement(); - } - - @NotNull - public Element serialize(@NotNull RootXNodeImpl rootxnode) throws SchemaException { - initialize(); - return serializeInternal(rootxnode, null); - } - - public Element serialize(@NotNull List roots, @Nullable QName aggregateElementName) throws SchemaException { - initialize(); - if (aggregateElementName == null) { - aggregateElementName = schemaRegistry.getPrismContext().getObjectsElementName(); - if (aggregateElementName == null) { - throw new IllegalStateException("Couldn't serialize list of objects because the aggregated element name is not set"); - } - } - Element aggregateElement = createElement(aggregateElementName, null); - for (RootXNodeImpl root : roots) { - serializeInternal(root, aggregateElement); - } - return aggregateElement; - } - - public Element serializeUnderElement(RootXNodeImpl rootxnode, Element parentElement) throws SchemaException { - initializeWithExistingDocument(parentElement.getOwnerDocument()); - return serializeInternal(rootxnode, parentElement); - } - - @NotNull - private Element serializeInternal(@NotNull RootXNodeImpl rootxnode, Element parentElement) throws SchemaException { - QName rootElementName = rootxnode.getRootElementName(); - Element topElement = createElement(rootElementName, parentElement); - if (parentElement != null) { - parentElement.appendChild(topElement); - } - QName typeQName = rootxnode.getTypeQName(); - if (typeQName != null && rootxnode.isExplicitTypeDeclaration()) { - DOMUtil.setXsiType(topElement, setQNamePrefixExplicitIfNeeded(typeQName)); - } - XNodeImpl subnode = rootxnode.getSubnode(); - if (subnode instanceof PrimitiveXNodeImpl) { - serializePrimitiveElementOrAttribute((PrimitiveXNodeImpl) subnode, topElement, rootElementName, false); - return DOMUtil.getFirstChildElement(topElement); - } else { - if (subnode instanceof MapXNodeImpl) { - // at this point we can put frequently used namespaces (e.g. c, t, q, ri) into the document to eliminate their use - // on many places inside the doc (MID-2198) - DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); - serializeMap((MapXNodeImpl) subnode, topElement); - } else if (subnode.isHeterogeneousList()) { - DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); - serializeExplicitList((ListXNodeImpl) subnode, topElement); - } else { - throw new SchemaException( - "Sub-root xnode is not a map nor an explicit list, cannot serialize to XML (it is " + subnode + ")"); - } - addDefaultNamespaceDeclaration(topElement); - return topElement; - } - } - - /** - * Adds xmlns='...common-3' if needed. - * In fact, this is VERY ugly hack to avoid putting common-3 declarations all over the elements in bulk actions response. - * e.getNamespaceURI returns something only if it was present during node creation. - * So, in fact, this code is more than fragile. Seems to work with the current XNode->DOM serialization, though. - */ - private void addDefaultNamespaceDeclaration(Element top) { - List prefixless = DOMUtil.getElementsWithoutNamespacePrefix(top); - if (prefixless.size() < 2) { - return; // nothing to do here - } - Set namespaces = prefixless.stream().map(e -> emptyIfNull(e.getNamespaceURI())).collect(Collectors.toSet()); - if (namespaces.size() != 1 || StringUtils.isEmpty(namespaces.iterator().next())) { - return; - } - DOMUtil.setNamespaceDeclaration(top, "", namespaces.iterator().next()); - } - - Element serializeToElement(MapXNodeImpl xmap, QName elementName) throws SchemaException { - initialize(); - Element element = createElement(elementName, null); - serializeMap(xmap, element); - return element; - } - - private void serializeMap(MapXNodeImpl xmap, Element topElement) throws SchemaException { - for (Entry entry: xmap.entrySet()) { - QName elementQName = entry.getKey(); - XNodeImpl xsubnode = entry.getValue(); - serializeSubnode(xsubnode, topElement, elementQName); - } - } - - private void serializeSubnode(XNodeImpl xsubnode, Element parentElement, QName elementName) throws SchemaException { - if (xsubnode == null) { - return; - } - if (xsubnode instanceof IncompleteMarkerXNode) { - Element element = createElement(elementName, parentElement); - DOMUtil.setAttributeValue(element, DOMUtil.IS_INCOMPLETE_ATTRIBUTE_NAME, "true"); - parentElement.appendChild(element); - } else if (xsubnode instanceof RootXNodeImpl) { - Element element = createElement(elementName, parentElement); - appendCommentIfPresent(element, xsubnode); - parentElement.appendChild(element); - serializeSubnode(((RootXNodeImpl) xsubnode).getSubnode(), element, ((RootXNodeImpl) xsubnode).getRootElementName()); - } else if (xsubnode instanceof MapXNodeImpl) { - Element element = createElement(elementName, parentElement); - appendCommentIfPresent(element, xsubnode); - if (xsubnode.isExplicitTypeDeclaration() && xsubnode.getTypeQName() != null) { - DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(xsubnode.getTypeQName())); - } - parentElement.appendChild(element); - serializeMap((MapXNodeImpl)xsubnode, element); - } else if (xsubnode instanceof PrimitiveXNodeImpl) { - PrimitiveXNodeImpl xprim = (PrimitiveXNodeImpl)xsubnode; - if (xprim.isAttribute()) { - serializePrimitiveElementOrAttribute(xprim, parentElement, elementName, true); - } else { - serializePrimitiveElementOrAttribute(xprim, parentElement, elementName, false); - } - } else if (xsubnode instanceof ListXNodeImpl) { - ListXNodeImpl xlist = (ListXNodeImpl)xsubnode; - if (xlist.isHeterogeneousList()) { - Element element = createElement(elementName, parentElement); - serializeExplicitList(xlist, element); - parentElement.appendChild(element); - } else { - for (XNodeImpl xsubsubnode : xlist) { - serializeSubnode(xsubsubnode, parentElement, elementName); - } - } - } else if (xsubnode instanceof SchemaXNodeImpl) { - serializeSchema((SchemaXNodeImpl)xsubnode, parentElement); - } else { - throw new IllegalArgumentException("Unknown subnode "+xsubnode); - } - } - - private void serializeExplicitList(ListXNodeImpl list, Element parent) throws SchemaException { - for (XNodeImpl node : list) { - if (node.getElementName() == null) { - throw new SchemaException("In a list, there are both nodes with element names and nodes without them: " + list); - } - serializeSubnode(node, parent, node.getElementName()); - } - DOMUtil.setAttributeValue(parent, DOMUtil.IS_LIST_ATTRIBUTE_NAME, "true"); - } - - Element serializeXPrimitiveToElement(PrimitiveXNodeImpl xprim, QName elementName) throws SchemaException { - initialize(); - Element parent = DOMUtil.createElement(doc, new QName("fake","fake")); - serializePrimitiveElementOrAttribute(xprim, parent, elementName, false); - return DOMUtil.getFirstChildElement(parent); - } - - private void serializePrimitiveElementOrAttribute(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, boolean asAttribute) throws SchemaException { - QName typeQName = xprim.getTypeQName(); - - // if typeQName is not explicitly specified, we try to determine it from parsed value - // TODO we should probably set typeQName when parsing the value... - if (typeQName == null && xprim.isParsed()) { - Object v = xprim.getValue(); - if (v != null) { - typeQName = XsdTypeMapper.toXsdType(v.getClass()); - } - } - - if (typeQName == null) { // this means that either xprim is unparsed or it is empty - if (PrismContextImpl.isAllowSchemalessSerialization()) { - // We cannot correctly serialize without a type. But this is needed - // sometimes. So just default to string - String stringValue = xprim.getStringValue(); - if (stringValue != null) { - if (asAttribute) { - DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), stringValue); - DOMUtil.setNamespaceDeclarations(parentElement, xprim.getRelevantNamespaceDeclarations()); - } else { - Element element; - try { - element = createElement(elementOrAttributeName, parentElement); - appendCommentIfPresent(element, xprim); - } catch (DOMException e) { - throw new DOMException(e.code, e.getMessage() + "; creating element "+elementOrAttributeName+" in element "+DOMUtil.getQName(parentElement)); - } - parentElement.appendChild(element); - DOMUtil.setElementTextContent(element, stringValue); - DOMUtil.setNamespaceDeclarations(element, xprim.getRelevantNamespaceDeclarations()); - } - } - return; - } else { - throw new IllegalStateException("No type for primitive element "+elementOrAttributeName+", cannot serialize (schemaless serialization is disabled)"); - } - } - - // typeName != null after this point - - if (QNameUtil.isUnqualified(typeQName)) { - typeQName = XsdTypeMapper.determineQNameWithNs(typeQName); - } - - Element element = null; - - if (ItemPathType.COMPLEX_TYPE.equals(typeQName)) { - //ItemPathType itemPathType = //ItemPathType.asItemPathType(xprim.getValue()); // TODO fix this hack - ItemPathType itemPathType = (ItemPathType) xprim.getValue(); - if (itemPathType != null) { - if (asAttribute) { - throw new UnsupportedOperationException("Serializing ItemPath as an attribute is not supported yet"); - } - element = serializeItemPathTypeToElement(itemPathType, elementOrAttributeName, parentElement.getOwnerDocument()); - parentElement.appendChild(element); - } - - } else { - // not an ItemPathType - - if (!asAttribute) { - try { - element = createElement(elementOrAttributeName, parentElement); - } catch (DOMException e) { - throw new DOMException(e.code, e.getMessage() + "; creating element "+elementOrAttributeName+" in element "+DOMUtil.getQName(parentElement)); - } - appendCommentIfPresent(element, xprim); - parentElement.appendChild(element); - } - - if (DOMUtil.XSD_QNAME.equals(typeQName)) { - QName value = (QName) xprim.getParsedValueWithoutRecording(DOMUtil.XSD_QNAME); - value = setQNamePrefixExplicitIfNeeded(value); - if (asAttribute) { - try { - DOMUtil.setQNameAttribute(parentElement, elementOrAttributeName.getLocalPart(), value); - } catch (DOMException e) { - throw new DOMException(e.code, e.getMessage() + "; setting attribute "+elementOrAttributeName.getLocalPart()+" in element "+DOMUtil.getQName(parentElement)+" to QName value "+value); - } - } else { - DOMUtil.setQNameValue(element, value); - } - } else { - // not ItemType nor QName - String value = xprim.getGuessedFormattedValue(); - String fixedValue = SerializationOptions.isEscapeInvalidCharacters(serializationOptions) ? - DOMUtil.escapeInvalidXmlCharsIfPresent(value) : value; - if (asAttribute) { - DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), fixedValue); - } else { - DOMUtil.setElementTextContent(element, fixedValue); - } - } - - } - if (!asAttribute && xprim.isExplicitTypeDeclaration()) { - DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(typeQName)); - } - } - - private void appendCommentIfPresent(Element element, XNodeImpl xnode) { - String text = xnode.getComment(); - if (StringUtils.isNotEmpty(text)) { - DOMUtil.createComment(element, text); - } - } - - private void serializeSchema(SchemaXNodeImpl xschema, Element parentElement) { - Element schemaElement = xschema.getSchemaElement(); - if (schemaElement == null){ - return; - } - Element clonedSchema = (Element) schemaElement.cloneNode(true); - doc.adoptNode(clonedSchema); - parentElement.appendChild(clonedSchema); - } - - /** - * Create XML element with the correct namespace prefix and namespace definition. - * @param qname element QName - * @return created DOM element - */ - @NotNull - private Element createElement(QName qname, Element parentElement) { - String namespaceURI = qname.getNamespaceURI(); - if (!StringUtils.isBlank(namespaceURI)) { - qname = setQNamePrefix(qname); - } - if (parentElement != null) { - return DOMUtil.createElement(doc, qname, parentElement, parentElement); - } else { - // This is needed otherwise the root element itself could not be created - // Caller of this method is responsible for setting the topElement - return DOMUtil.createElement(doc, qname); - } - } - - private QName setQNamePrefix(QName qname) { - DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); - if (namespacePrefixMapper == null) { - return qname; - } - return namespacePrefixMapper.setQNamePrefix(qname); - } - - private QName setQNamePrefixExplicitIfNeeded(QName name) { - if (name != null && StringUtils.isNotBlank(name.getNamespaceURI()) && StringUtils.isBlank(name.getPrefix())) { - return setQNamePrefixExplicit(name); - } else { - return name; - } - } - - private QName setQNamePrefixExplicit(QName qname) { - DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); - if (namespacePrefixMapper == null) { - return qname; - } - return namespacePrefixMapper.setQNamePrefixExplicit(qname); - } - - public Element serializeItemPathTypeToElement(ItemPathType itemPathType, QName elementName, Document ownerDocument) { - return ItemPathHolder.serializeToElement(itemPathType.getItemPath(), elementName, ownerDocument); - } - -} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java new file mode 100644 index 00000000000..72d8d8bea8e --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java @@ -0,0 +1,330 @@ +/* + * 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.midpoint.prism.impl.lex.dom; + +import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.PrismConstants; +import com.evolveum.midpoint.prism.impl.xnode.*; + +import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.MetadataAware; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; + +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * TODO + */ +class DomReader { + + public static final Trace LOGGER = TraceManager.getTrace(DomLexicalProcessor.class); + + private static final QName SCHEMA_ELEMENT_QNAME = DOMUtil.XSD_SCHEMA_ELEMENT; + static final String VALUE_LOCAL_PART = "_value"; + static final String METADATA_LOCAL_PART = "_metadata"; + + private final Element root; + private final QName rootElementName; + private final SchemaRegistry schemaRegistry; + + @NotNull private final QName valueElementName; + @NotNull private final QName metadataElementName; + + DomReader(@NotNull Element root, SchemaRegistry schemaRegistry) { + this.root = root; + this.rootElementName = DOMUtil.getQName(root); + this.schemaRegistry = schemaRegistry; + this.valueElementName = new QName(schemaRegistry.getDefaultNamespace(), VALUE_LOCAL_PART); + this.metadataElementName = new QName(schemaRegistry.getDefaultNamespace(), DomReader.METADATA_LOCAL_PART); + } + + DomReader(Document document, SchemaRegistry schemaRegistry) { + this(DOMUtil.getFirstChildElement(document), schemaRegistry); + } + + @NotNull + List readObjects() throws SchemaException { + if (rootIsNotObjectsMarker()) { + return Collections.singletonList(read()); + } else { + List rv = new ArrayList<>(); + for (Element child : DOMUtil.listChildElements(root)) { + rv.add(new DomReader(child, schemaRegistry).read()); + } + return rv; + } + } + + @NotNull RootXNodeImpl read() throws SchemaException { + RootXNodeImpl xroot = new RootXNodeImpl(rootElementName); + XNodeImpl xnode = readElementContent(root, rootElementName, false); + xroot.setSubnode(xnode); + return xroot; + } + + private boolean rootIsNotObjectsMarker() { + QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); + return objectsMarker != null && !QNameUtil.match(rootElementName, objectsMarker); + } + + /** + * Reads the content of the element. + * + * @param knownElementName Pre-fetched element name. Might be null (this is expected if storeElementName is true). + */ + @NotNull + private XNodeImpl readElementContent(@NotNull Element element, QName knownElementName, boolean storeElementName) throws SchemaException { + XNodeImpl node; + + QName xsiType = DOMUtil.resolveXsiType(element); + QName elementName = knownElementName != null ? knownElementName : DOMUtil.getQName(element); + + Element valueChild = DOMUtil.getMatchingChildElement(element, valueElementName); + if (valueChild != null) { + node = readElementContent(valueChild, elementName, false); + } else if (DOMUtil.hasChildElements(element) || DOMUtil.hasApplicationAttributes(element)) { + if (isList(element, elementName, xsiType)) { + node = readElementContentToList(element); + } else { + node = readElementContentToMap(element); + } + } else if (DOMUtil.isMarkedAsIncomplete(element)) { + // Note that it is of no use to check for "incomplete" on non-leaf elements. In XML the incomplete attribute + // must be attached to an empty element. + node = new IncompleteMarkerXNodeImpl(); + } else { + node = parsePrimitiveElement(element); + } + readMetadata(element, node); + readMaxOccurs(element, node); + + setTypeAndElementName(xsiType, elementName, node, storeElementName); + return node; + } + + private void readMetadata(@NotNull Element element, XNodeImpl node) throws SchemaException { + Element metadataChild = DOMUtil.getMatchingChildElement(element, metadataElementName); + if (metadataChild != null) { + XNodeImpl metadata = readElementContent(metadataChild, null, false); + if (metadata instanceof MapXNode) { + if (node instanceof MetadataAware) { + ((MetadataAware) node).setMetadataNode((MapXNode) metadata); + } else { + throw new SchemaException("Attempt to add metadata to non-metadata-aware XNode: " + node); + } + } else { + throw new SchemaException("Metadata is not of Map type: " + metadata); + } + } + } + + private void readMaxOccurs(Element element, XNodeImpl xnode) throws SchemaException { + String maxOccursString = element.getAttributeNS( + PrismConstants.A_MAX_OCCURS.getNamespaceURI(), + PrismConstants.A_MAX_OCCURS.getLocalPart()); + if (!StringUtils.isBlank(maxOccursString)) { + int maxOccurs = parseMultiplicity(maxOccursString, element); + xnode.setMaxOccurs(maxOccurs); + } + } + + private int parseMultiplicity(String maxOccursString, Element element) throws SchemaException { + if (PrismConstants.MULTIPLICITY_UNBOUNDED.equals(maxOccursString)) { + return -1; + } + if (maxOccursString.startsWith("-")) { + return -1; + } + if (StringUtils.isNumeric(maxOccursString)) { + return Integer.parseInt(maxOccursString); + } else { + throw new SchemaException("Expected numeric value for " + PrismConstants.A_MAX_OCCURS.getLocalPart() + + " attribute on " + DOMUtil.getQName(element) + " but got " + maxOccursString); + } + } + + private void setTypeAndElementName(QName xsiType, QName elementName, XNodeImpl xnode, boolean storeElementName) { + if (xsiType != null) { + xnode.setTypeQName(xsiType); + xnode.setExplicitTypeDeclaration(true); + } + if (storeElementName) { + xnode.setElementName(elementName); + } + } + + // all the sub-elements should be compatible (this is not enforced here, however) + private ListXNodeImpl readElementContentToList(Element element) throws SchemaException { + if (DOMUtil.hasApplicationAttributes(element)) { + throw new SchemaException("List should have no application attributes: " + element); + } + return parseElementList(DOMUtil.listChildElements(element), null, true); + } + + private MapXNodeImpl readElementContentToMap(Element element) throws SchemaException { + MapXNodeImpl xmap = new MapXNodeImpl(); + + // Attributes + for (Attr attr : DOMUtil.listApplicationAttributes(element)) { + QName attrQName = DOMUtil.getQName(attr); + XNodeImpl subnode = parseAttributeValue(attr); + xmap.put(attrQName, subnode); + } + + // Sub-elements + QName lastElementName = null; + List lastElements = null; + for (Element childElement : DOMUtil.listChildElements(element)) { + QName childName = DOMUtil.getQName(childElement); + if (QNameUtil.match(childName, metadataElementName)) { + continue; + } + if (!match(childName, lastElementName)) { + parseSubElementsGroupAsMapEntry(xmap, lastElementName, lastElements); + lastElementName = childName; + lastElements = new ArrayList<>(); + } + lastElements.add(childElement); + } + parseSubElementsGroupAsMapEntry(xmap, lastElementName, lastElements); + return xmap; + } + + private boolean isList(@NotNull Element element, @NotNull QName elementName, @Nullable QName xsiType) { + String isListAttribute = DOMUtil.getAttribute(element, DOMUtil.IS_LIST_ATTRIBUTE_NAME); + if (StringUtils.isNotEmpty(isListAttribute)) { + return Boolean.parseBoolean(isListAttribute); + } + // enable this after schema registry is optional (now it's mandatory) +// if (schemaRegistry == null) { +// return false; +// } + + SchemaRegistry.IsList fromSchema = schemaRegistry.isList(xsiType, elementName); + if (fromSchema != SchemaRegistry.IsList.MAYBE) { + return fromSchema == SchemaRegistry.IsList.YES; + } + + // checking the content + if (DOMUtil.hasApplicationAttributes(element)) { + return false; // TODO - or should we fail in this case? + } + //System.out.println("Elements are compatible: " + DOMUtil.listChildElements(element) + ": " + rv); + return elementsAreCompatible(DOMUtil.listChildElements(element)); + } + + private boolean elementsAreCompatible(List elements) { + QName unified = null; + for (Element element : elements) { + QName root = getHierarchyRoot(DOMUtil.getQName(element)); + if (unified == null) { + unified = root; + } else if (!QNameUtil.match(unified, root)) { + return false; + } else if (QNameUtil.noNamespace(unified) && QNameUtil.hasNamespace(root)) { + unified = root; + } + } + return true; + } + + private QName getHierarchyRoot(QName name) { + ItemDefinition def = schemaRegistry.findItemDefinitionByElementName(name); + if (def == null || !def.isHeterogeneousListItem()) { + return name; + } else { + return def.getSubstitutionHead(); + } + } + + private boolean match(QName name, QName existing) { + if (existing == null) { + return false; + } else { + return QNameUtil.match(name, existing); + } + } + + // All elements share the same elementName + private void parseSubElementsGroupAsMapEntry(MapXNodeImpl xmap, QName elementName, List elements) throws SchemaException { + if (elements == null || elements.isEmpty()) { + return; + } + XNodeImpl xsub; + // We really want to have equals here, not match + // we want to be very explicit about namespace here + if (elementName.equals(SCHEMA_ELEMENT_QNAME)) { + if (elements.size() == 1) { + xsub = parseSchemaElement(elements.iterator().next()); + } else { + throw new SchemaException("Too many schema elements"); + } + } else if (elements.size() == 1) { + xsub = readElementContent(elements.get(0), elementName, false); + } else { + xsub = parseElementList(elements, elementName, false); + } + xmap.merge(elementName, xsub); + } + + /** + * Parses elements that should form the list. + *

+ * Either they have the same element name, or they are stored as a sub-elements of "list" parent element. + */ + @NotNull + @Contract("!null, null, false -> fail") + private ListXNodeImpl parseElementList(List elements, QName elementName, boolean storeElementNames) throws SchemaException { + if (!storeElementNames && elementName == null) { + throw new IllegalArgumentException("When !storeElementNames the element name must be specified"); + } + ListXNodeImpl xlist = new ListXNodeImpl(); + for (Element element : elements) { + xlist.add(readElementContent(element, elementName, storeElementNames)); + } + return xlist; + } + + // @pre element has no children nor application attributes + private PrimitiveXNodeImpl parsePrimitiveElement(@NotNull Element element) { + PrimitiveXNodeImpl xnode = new PrimitiveXNodeImpl<>(); + xnode.setValueParser(new ElementValueParser<>(element)); + return xnode; + } + + private PrimitiveXNodeImpl parseAttributeValue(@NotNull Attr attr) { + PrimitiveXNodeImpl xnode = new PrimitiveXNodeImpl<>(); + xnode.setValueParser(new AttributeValueParser<>(attr)); + xnode.setAttribute(true); + return xnode; + } + + @NotNull + private SchemaXNodeImpl parseSchemaElement(Element schemaElement) { + SchemaXNodeImpl xschema = new SchemaXNodeImpl(); + xschema.setSchemaElement(schemaElement); + return xschema; + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java new file mode 100644 index 00000000000..2227fd87b72 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java @@ -0,0 +1,437 @@ +/* + * 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.prism.impl.lex.dom; + +import com.evolveum.midpoint.prism.SerializationContext; +import com.evolveum.midpoint.prism.SerializationOptions; +import com.evolveum.midpoint.prism.impl.PrismContextImpl; +import com.evolveum.midpoint.prism.impl.marshaller.ItemPathHolder; +import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.prism.xml.DynamicNamespacePrefixMapper; +import com.evolveum.midpoint.prism.xml.XsdTypeMapper; +import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.SchemaXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.evolveum.midpoint.prism.xnode.IncompleteMarkerXNode; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.RootXNode; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.evolveum.midpoint.prism.impl.lex.dom.DomReader.VALUE_LOCAL_PART; +import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; + +/** + * @author semancik + * + */ +class DomWriter { + + @NotNull private final Document document; + @NotNull private final SchemaRegistry schemaRegistry; + private final SerializationOptions serializationOptions; + + @NotNull private final QName valueElementName; + @NotNull private final QName metadataElementName; + + DomWriter(@NotNull SchemaRegistry schemaRegistry, SerializationContext context) { + this.document = DOMUtil.getDocument(); + this.schemaRegistry = schemaRegistry; + this.serializationOptions = context != null ? context.getOptions() : null; + this.valueElementName = new QName(schemaRegistry.getDefaultNamespace(), VALUE_LOCAL_PART); + this.metadataElementName = new QName(schemaRegistry.getDefaultNamespace(), DomReader.METADATA_LOCAL_PART); + } + + @NotNull + Element writeRoot(@NotNull RootXNode root) throws SchemaException { + return writeRootInternal((RootXNodeImpl) root, null); + } + + Element writeRoots(@NotNull List roots) throws SchemaException { + QName aggregateElementName = schemaRegistry.getPrismContext().getObjectsElementName(); + if (aggregateElementName == null) { + throw new IllegalStateException("Couldn't serialize list of objects because the aggregated element name is not set"); + } + Element aggregateElement = createElement(aggregateElementName, null); + for (RootXNodeImpl root : roots) { + writeRootInternal(root, aggregateElement); + } + return aggregateElement; + } + + /** + * Seems to be used in strange circumstances (called from various hacks). + * To be reconsidered eventually. Avoid using in new code. + */ + @Deprecated + Element writeMap(MapXNodeImpl xmap, QName elementName) throws SchemaException { + Element element = createElement(elementName, null); + writeMap(xmap, element); + return element; + } + + /** + * TODO There is an overlap in functionality between this method and writeNode. + * We should reconcile that somehow eventually. + * But beware, there are also significant differences, e.g. + * - we set "global" namespace declarations and default namespace declaration here + * - when serializing primitives we insist on element representation (not attribute one) + */ + @NotNull + private Element writeRootInternal(@NotNull RootXNodeImpl root, Element parentElement) throws SchemaException { + QName rootElementName = root.getRootElementName(); + Element rootElement = createAndAppendChild(rootElementName, parentElement); + + XNodeImpl subnode = root.getSubnode(); + if (subnode instanceof PrimitiveXNodeImpl) { + writePrimitive((PrimitiveXNodeImpl) subnode, rootElement, rootElementName, false); + return DOMUtil.getFirstChildElement(rootElement); + } else { + // At this point we can put frequently used namespaces (e.g. c, t, q, ri) into the document to eliminate their use + // on many places inside the doc (MID-2198) + DOMUtil.setNamespaceDeclarations(rootElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); + if (subnode instanceof MapXNodeImpl) { + writeMap((MapXNodeImpl) subnode, rootElement); + } else if (subnode.isHeterogeneousList()) { + writeHeterogeneousList((ListXNodeImpl) subnode, rootElement); + } else { + throw new SchemaException( + "Sub-root xnode is not a map nor an explicit list, cannot serialize to XML (it is " + subnode + ")"); + } + addDefaultNamespaceDeclaration(rootElement); + return rootElement; + } + } + + /** + * Adds xmlns='...common-3' if needed. + * In fact, this is VERY ugly hack to avoid putting common-3 declarations all over the elements in bulk actions response. + * e.getNamespaceURI returns something only if it was present during node creation. + * So, in fact, this code is more than fragile. Seems to work with the current XNode->DOM serialization, though. + */ + private void addDefaultNamespaceDeclaration(Element top) { + List prefixLess = DOMUtil.getElementsWithoutNamespacePrefix(top); + if (prefixLess.size() < 2) { + return; // nothing to do here + } + Set namespaces = prefixLess.stream() + .map(e -> emptyIfNull(e.getNamespaceURI())) + .collect(Collectors.toSet()); + if (namespaces.size() == 1) { + String defaultNamespace = namespaces.iterator().next(); + if (StringUtils.isNotEmpty(defaultNamespace)) { + DOMUtil.setNamespaceDeclaration(top, "", defaultNamespace); + } + } + } + + private void writeMap(MapXNodeImpl xmap, Element parent) throws SchemaException { + MapXNode metadataNode = xmap.getMetadataNode(); + if (metadataNode != null) { + writeMetadata(metadataNode, parent); + } + for (Entry entry: xmap.entrySet()) { + QName elementQName = entry.getKey(); + XNodeImpl xsubnode = entry.getValue(); + writeNode(xsubnode, parent, elementQName); + } + setXsiTypeIfNeeded(xmap, parent); + } + + private void writeNode(XNodeImpl node, Element parentElement, QName elementName) throws SchemaException { + if (node == null) { + return; + } + if (node instanceof IncompleteMarkerXNode) { + Element child = createAndAppendChild(elementName, parentElement); + DOMUtil.setAttributeValue(child, DOMUtil.IS_INCOMPLETE_ATTRIBUTE_NAME, "true"); + } else if (node instanceof RootXNodeImpl) { + throw new IllegalStateException("Shouldn't be here!"); + // TODO remove eventually +// Element child = createAndAppendChild(elementName, parentElement); +// appendCommentIfPresent(child, node); +// writeNode(((RootXNodeImpl) node).getSubnode(), child, ((RootXNodeImpl) node).getRootElementName()); + } else if (node instanceof MapXNodeImpl) { + Element child = createAndAppendChild(elementName, parentElement); + appendCommentIfPresent(child, node); + writeMap((MapXNodeImpl)node, child); + } else if (node instanceof PrimitiveXNodeImpl) { + PrimitiveXNodeImpl xprim = (PrimitiveXNodeImpl)node; + writePrimitive(xprim, parentElement, elementName, xprim.isAttribute()); + } else if (node instanceof ListXNodeImpl) { + ListXNodeImpl list = (ListXNodeImpl) node; + if (list.isHeterogeneousList()) { + Element child = createAndAppendChild(elementName, parentElement); + writeHeterogeneousList(list, child); + } else { + for (XNodeImpl listItem : list) { + writeNode(listItem, parentElement, elementName); + } + } + } else if (node instanceof SchemaXNodeImpl) { + writeSchema((SchemaXNodeImpl)node, parentElement); + } else { + throw new IllegalArgumentException("Unknown subnode "+node); + } + } + + private void writeHeterogeneousList(ListXNodeImpl list, Element parent) throws SchemaException { + for (XNodeImpl listItem : list) { + if (listItem.getElementName() == null) { + throw new SchemaException("In a list, there are both nodes with element names and nodes without them: " + list); + } + writeNode(listItem, parent, listItem.getElementName()); + } + DOMUtil.setAttributeValue(parent, DOMUtil.IS_LIST_ATTRIBUTE_NAME, "true"); + setXsiTypeIfNeeded(list, parent); + } + + private void writePrimitive(PrimitiveXNodeImpl xprim, Element parentElement, + QName elementOrAttributeName, boolean asAttribute) throws SchemaException { + if (xprim.hasMetadata()) { + Element valuePlusMetadataElement = createAndAppendChild(elementOrAttributeName, parentElement); + writePrimitiveValue(xprim, valuePlusMetadataElement, valueElementName, false); + writeMetadata(xprim.getMetadataNode(), valuePlusMetadataElement); + } else { + writePrimitiveValue(xprim, parentElement, elementOrAttributeName, asAttribute); + } + } + + private void writeMetadata(MapXNode metadataNode, Element parentElement) throws SchemaException { + Element metadataElement = createAndAppendChild(metadataElementName, parentElement); + writeMap((MapXNodeImpl) metadataNode, metadataElement); + } + + private void writePrimitiveValue(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, + boolean asAttribute) throws SchemaException { + QName typeName = determineTypeName(xprim); + + if (typeName == null) { // this means that either xprim is unparsed or it is empty + writePrimitiveWithoutType(xprim, parentElement, elementOrAttributeName, asAttribute); + } else { + writePrimitiveWithType(xprim, parentElement, elementOrAttributeName, asAttribute, typeName); + } + } + + private void writePrimitiveWithoutType(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, + boolean asAttribute) { + if (!PrismContextImpl.isAllowSchemalessSerialization()) { + throw new IllegalStateException("No type for primitive element "+elementOrAttributeName+", cannot serialize (schemaless serialization is disabled)"); + } + + // We cannot correctly serialize without a type. But this is needed sometimes. So just default to string. + String stringValue = xprim.getStringValue(); + if (stringValue != null) { + if (asAttribute) { + DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), stringValue); + DOMUtil.setNamespaceDeclarations(parentElement, xprim.getRelevantNamespaceDeclarations()); + } else { + Element element = createAndAppendChild(elementOrAttributeName, parentElement); + appendCommentIfPresent(element, xprim); + DOMUtil.setElementTextContent(element, stringValue); + DOMUtil.setNamespaceDeclarations(element, xprim.getRelevantNamespaceDeclarations()); + } + } + } + + private void writePrimitiveWithType(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, + boolean asAttribute, QName typeName) throws SchemaException { + Element element; + // Note that item paths and QNames are special because of prefixes. + if (ItemPathType.COMPLEX_TYPE.equals(typeName)) { + if (asAttribute) { + throw new UnsupportedOperationException("Serializing ItemPath as an attribute is not supported yet"); + } else { + element = writeItemPath(xprim, parentElement, elementOrAttributeName); + } + } else { + if (!asAttribute) { + element = createAndAppendChild(elementOrAttributeName, parentElement); + appendCommentIfPresent(element, xprim); + } else { + element = null; + } + + if (DOMUtil.XSD_QNAME.equals(typeName)) { + QName value = (QName) xprim.getParsedValueWithoutRecording(DOMUtil.XSD_QNAME); + QName valueWithPrefix = setQNamePrefixExplicitIfNeeded(value); + if (asAttribute) { + setQNameAttribute(parentElement, elementOrAttributeName.getLocalPart(), valueWithPrefix); + } else { + DOMUtil.setQNameValue(element, valueWithPrefix); + } + } else { + // not ItemType nor QName + String value = xprim.getGuessedFormattedValue(); + String fixedValue = SerializationOptions.isEscapeInvalidCharacters(serializationOptions) ? + DOMUtil.escapeInvalidXmlCharsIfPresent(value) : value; + if (asAttribute) { + DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), fixedValue); + } else { + DOMUtil.setElementTextContent(element, fixedValue); + } + } + } + if (!asAttribute) { + setXsiTypeIfNeeded(xprim, element, typeName); + } + } + + private void setQNameAttribute(Element parentElement, String attributeName, QName value) { + try { + DOMUtil.setQNameAttribute(parentElement, attributeName, value); + } catch (DOMException e) { + throw new DOMException(e.code, e.getMessage() + "; setting attribute "+attributeName+" in element "+DOMUtil.getQName(parentElement)+" to QName value "+value); + } + } + + @Nullable + private Element writeItemPath(PrimitiveXNodeImpl xprim, Element parent, QName elementName) { + ItemPathType itemPathType = (ItemPathType) xprim.getValue(); + if (itemPathType != null) { + Element element = ItemPathHolder.serializeToElement(itemPathType.getItemPath(), elementName, document); + parent.appendChild(element); + return element; + } else { + return null; + } + } + + @Nullable + private QName determineTypeName(PrimitiveXNodeImpl xprim) { + QName explicitTypeName = xprim.getTypeQName(); + if (explicitTypeName != null) { + return qualifyIfNeeded(explicitTypeName); + } + + // Ff typeQName is not explicitly specified, we try to determine it from parsed value. + // We should probably set typeQName when parsing the value... + if (xprim.isParsed()) { + Object v = xprim.getValue(); + if (v != null) { + return XsdTypeMapper.toXsdType(v.getClass()); + } + } + + return null; + } + + @Nullable + private QName qualifyIfNeeded(QName explicitTypeName) { + if (QNameUtil.isUnqualified(explicitTypeName)) { + return XsdTypeMapper.determineQNameWithNs(explicitTypeName); + } else { + return explicitTypeName; + } + } + + private void appendCommentIfPresent(Element element, XNodeImpl xnode) { + String text = xnode.getComment(); + if (StringUtils.isNotEmpty(text)) { + DOMUtil.createComment(element, text); + } + } + + private void writeSchema(SchemaXNodeImpl xschema, Element parentElement) { + Element schemaElement = xschema.getSchemaElement(); + if (schemaElement != null) { + Element clonedSchemaElement = (Element) schemaElement.cloneNode(true); + document.adoptNode(clonedSchemaElement); + parentElement.appendChild(clonedSchemaElement); + } + } + + /** + * Create XML element with the correct namespace prefix and namespace definition. + * @param qname element QName + * @return created DOM element + */ + @NotNull + private Element createElement(QName qname, Element parentElement) { + try { + String namespaceURI = qname.getNamespaceURI(); + if (!StringUtils.isBlank(namespaceURI)) { + qname = setQNamePrefix(qname); + } + if (parentElement != null) { + return DOMUtil.createElement(document, qname, parentElement, parentElement); + } else { + // This is needed otherwise the root element itself could not be created + // Caller of this method is responsible for setting the topElement + return DOMUtil.createElement(document, qname); + } + } catch (DOMException e) { + throw new DOMException(e.code, e.getMessage() + "; creating element " + qname + " in element " + DOMUtil.getQName(parentElement)); + } + } + + @NotNull + private Element createAndAppendChild(QName childName, Element parent) { + Element child = createElement(childName, parent); + if (parent != null) { + parent.appendChild(child); + } + return child; + } + + private QName setQNamePrefix(QName qname) { + DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); + if (namespacePrefixMapper == null) { + return qname; + } + return namespacePrefixMapper.setQNamePrefix(qname); + } + + private QName setQNamePrefixExplicitIfNeeded(QName name) { + if (name != null && StringUtils.isNotBlank(name.getNamespaceURI()) && StringUtils.isBlank(name.getPrefix())) { + return setQNamePrefixExplicit(name); + } else { + return name; + } + } + + private QName setQNamePrefixExplicit(QName qname) { + DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); + if (namespacePrefixMapper == null) { + return qname; + } + return namespacePrefixMapper.setQNamePrefixExplicit(qname); + } + + private DynamicNamespacePrefixMapper getNamespacePrefixMapper() { + return schemaRegistry.getNamespacePrefixMapper(); + } + + private void setXsiTypeIfNeeded(@NotNull XNodeImpl node, Element element) { + if (node.getTypeQName() != null && node.isExplicitTypeDeclaration()) { + DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(node.getTypeQName())); + } + } + + private void setXsiTypeIfNeeded(@NotNull XNodeImpl node, Element element, QName typeName) { + if (node.isExplicitTypeDeclaration()) { + DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(typeName)); + } + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java deleted file mode 100644 index e2ad61dd7e8..00000000000 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java +++ /dev/null @@ -1,935 +0,0 @@ -/* - * 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.prism.impl.lex.json; - -import java.io.*; -import java.util.*; -import java.util.Map.Entry; - -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.impl.ParsingContextImpl; -import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; -import com.evolveum.midpoint.prism.impl.lex.LexicalUtils; -import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLGenerator; -import com.evolveum.midpoint.prism.impl.xnode.*; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.prism.xnode.*; -import com.evolveum.midpoint.util.logging.LoggingUtils; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.fasterxml.jackson.core.*; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang3.Validate; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import com.evolveum.midpoint.prism.path.UniformItemPath; -import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import org.w3c.dom.Element; - -public abstract class AbstractJsonLexicalProcessor implements LexicalProcessor { - - private static final Trace LOGGER = TraceManager.getTrace(AbstractJsonLexicalProcessor.class); - - private static final String PROP_NAMESPACE = "@ns"; - private static final QName PROP_NAMESPACE_QNAME = new QName(PROP_NAMESPACE); - private static final String PROP_TYPE = "@type"; - private static final QName PROP_TYPE_QNAME = new QName(PROP_TYPE); - private static final String PROP_INCOMPLETE = "@incomplete"; - private static final QName PROP_INCOMPLETE_QNAME = new QName(PROP_INCOMPLETE); - private static final String PROP_ELEMENT = "@element"; - private static final QName PROP_ELEMENT_QNAME = new QName(PROP_ELEMENT); - private static final String PROP_VALUE = "@value"; - private static final QName PROP_VALUE_QNAME = new QName(PROP_VALUE); - - private static final String DEFAULT_NAMESPACE_MARKER = "##DEFAULT-NAMESPACE##"; - - @NotNull protected final SchemaRegistry schemaRegistry; - - AbstractJsonLexicalProcessor(@NotNull SchemaRegistry schemaRegistry) { - this.schemaRegistry = schemaRegistry; - } - - //region Parsing implementation - - @NotNull - @Override - public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { - List nodes = readInternal(source, parsingContext); - if (nodes.isEmpty()) { - throw new SchemaException("No data at input"); // shouldn't occur, because it is already treated in the called method - } else if (nodes.size() > 1) { - throw new SchemaException("More than one object found: " + nodes); - } else { - return nodes.get(0); - } - } - - /** - * Honors multi-document files and multiple objects in a single document ('c:objects', list-as-root mechanisms). - */ - @NotNull - @Override - public List readObjects(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { - return readInternal(source, parsingContext); - } - - @NotNull - private List readInternal(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { - InputStream is = source.getInputStream(); - try { - JsonParser parser = createJacksonParser(is); - return parseFromStart(parser, parsingContext, null); - } finally { - if (source.closeStreamAfterParsing()) { - closeQuietly(is); - } - } - } - - private void closeQuietly(InputStream is) { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - LoggingUtils.logExceptionAsWarning(LOGGER, "Couldn't close the input stream", e); - } - } - } - - private static final class IterativeParsingContext { - private final RootXNodeHandler handler; - private boolean dataSent; // true if we really found the list of objects and sent it out - private String defaultNamespace; // default namespace, if present - private boolean abortProcessing; // used when handler returns 'do not continue' - - private IterativeParsingContext(RootXNodeHandler handler) { - this.handler = handler; - } - } - - @Override - public void readObjectsIteratively(@NotNull ParserSource source, @NotNull ParsingContext parsingContext, - RootXNodeHandler handler) throws SchemaException, IOException { - InputStream is = source.getInputStream(); - try { - JsonParser parser = createJacksonParser(is); - parseFromStart(parser, parsingContext, handler); - } finally { - if (source.closeStreamAfterParsing()) { - closeQuietly(is); - } - } - } - - protected abstract JsonParser createJacksonParser(InputStream stream) throws SchemaException, IOException; - - static class JsonParsingContext { - @NotNull final JsonParser parser; - @NotNull private final ParsingContextImpl prismParsingContext; - - // TODO consider getting rid of these IdentityHashMaps by support default namespace marking and resolution - // directly in XNode structures (like it was done for Map XNode keys recently). - - // Definitions of namespaces ('@ns') within maps; to be applied after parsing. - @NotNull private final IdentityHashMap defaultNamespaces = new IdentityHashMap<>(); - // Elements that should be skipped when filling-in default namespaces - those that are explicitly set with no-NS ('#name'). - // (Values for these entries are not important. Only key presence is relevant.) - @NotNull private final IdentityHashMap noNamespaceElementNames = new IdentityHashMap<>(); - private JsonParsingContext(@NotNull JsonParser parser, @NotNull ParsingContextImpl prismParsingContext) { - this.parser = parser; - this.prismParsingContext = prismParsingContext; - } - - private JsonParsingContext createChildContext() { - return new JsonParsingContext(parser, prismParsingContext); - } - } - - @NotNull - private List parseFromStart(JsonParser unconfiguredParser, ParsingContext parsingContext, - RootXNodeHandler handler) throws SchemaException { - List rv = new ArrayList<>(); - JsonParsingContext ctx = null; - try { - JsonParser parser = configureParser(unconfiguredParser); - parser.nextToken(); - if (parser.currentToken() == null) { - throw new SchemaException("Nothing to parse: the input is empty."); - } - do { - ctx = new JsonParsingContext(parser, (ParsingContextImpl) parsingContext); - IterativeParsingContext ipc = handler != null ? new IterativeParsingContext(handler) : null; - XNodeImpl xnode = parseValue(ctx, ipc); - if (ipc != null && ipc.dataSent) { - // all the objects were sent to the handler, nothing more to do - } else { - List roots = valueToRootList(xnode, null, ctx); - if (ipc == null) { - rv.addAll(roots); - } else { - for (RootXNodeImpl root : roots) { - if (!ipc.handler.handleData(root)) { - ipc.abortProcessing = true; - break; - } - } - } - } - if (ipc != null && ipc.abortProcessing) { - // set either here or in parseValue - break; - } - } while (ctx.parser.nextToken() != null); // for multi-document YAML files - return rv; - } catch (IOException e) { - throw new SchemaException("Cannot parse JSON/YAML object: " + e.getMessage() + getPositionSuffixIfPresent(ctx), e); - } - } - - @NotNull - private List valueToRootList(XNodeImpl node, String defaultNamespace, JsonParsingContext ctx) throws SchemaException, IOException { - List rv = new ArrayList<>(); - if (node instanceof ListXNodeImpl) { - ListXNodeImpl list = (ListXNodeImpl) node; - for (XNodeImpl listItem : list) { - rv.addAll(valueToRootList(listItem, defaultNamespace, ctx)); - } - } else { - QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); - RootXNodeImpl root = postProcessValueToRoot(node, defaultNamespace, ctx); - if (root.getSubnode() instanceof ListXNodeImpl && objectsMarker != null && QNameUtil.match(objectsMarker, root.getRootElementName())) { - return valueToRootList(root.getSubnode(), defaultNamespace, ctx); - } else { - rv.add(root); - } - } - return rv; - } - - @NotNull - private RootXNodeImpl postProcessValueToRoot(XNodeImpl xnode, String defaultNamespace, JsonParsingContext ctx) throws SchemaException, IOException { - if (!xnode.isSingleEntryMap()) { - throw new SchemaException("Expected MapXNode with a single key; got " + xnode + " instead. At " + getPositionSuffix(ctx)); - } - processDefaultNamespaces(xnode, defaultNamespace, ctx); - processSchemaNodes(xnode); - Entry entry = ((MapXNodeImpl) xnode).entrySet().iterator().next(); - RootXNodeImpl root = new RootXNodeImpl(entry.getKey(), entry.getValue()); - if (entry.getValue() != null) { - root.setTypeQName(entry.getValue().getTypeQName()); // TODO - ok ???? - } - return root; - } - - @NotNull - private String getPositionSuffixIfPresent(JsonParsingContext ctx) { - return ctx != null ? " At: " + getPositionSuffix(ctx) : ""; - } - - // Default namespaces (@ns properties) are processed in the second pass, because they might be present within an object - // at any place, even at the end. - private void processDefaultNamespaces(XNodeImpl xnode, String parentDefault, JsonParsingContext ctx) { - if (xnode instanceof MapXNodeImpl) { - MapXNodeImpl map = (MapXNodeImpl) xnode; - String currentDefault = ctx.defaultNamespaces.getOrDefault(map, parentDefault); - map.replaceDefaultNamespaceMarkers(DEFAULT_NAMESPACE_MARKER, currentDefault); - for (Entry entry : map.entrySet()) { - processDefaultNamespaces(entry.getValue(), currentDefault, ctx); - } - qualifyElementNameIfNeeded(map, currentDefault, ctx); - } else { - qualifyElementNameIfNeeded(xnode, parentDefault, ctx); - if (xnode instanceof ListXNodeImpl) { - for (XNodeImpl item : (ListXNodeImpl) xnode) { - processDefaultNamespaces(item, parentDefault, ctx); - } - } - } - } - - private void qualifyElementNameIfNeeded(XNodeImpl node, String namespace, JsonParsingContext ctx) { - if (node.getElementName() != null - && QNameUtil.noNamespace(node.getElementName()) - && StringUtils.isNotEmpty(namespace) - && !ctx.noNamespaceElementNames.containsKey(node)) { - node.setElementName(new QName(namespace, node.getElementName().getLocalPart())); - } - } - - // Schema nodes can be detected only after namespaces are resolved. - // We simply convert primitive nodes to schema ones. - private void processSchemaNodes(XNodeImpl xnode) throws SchemaException, IOException { - if (xnode instanceof MapXNodeImpl) { - MapXNodeImpl map = (MapXNodeImpl) xnode; - XNodeImpl schemaNode = null; - for (Entry entry : map.entrySet()) { - QName fieldName = entry.getKey(); - XNodeImpl subnode = entry.getValue(); - if (DOMUtil.XSD_SCHEMA_ELEMENT.equals(fieldName)) { - schemaNode = subnode; - } else { - processSchemaNodes(subnode); - } - } - if (schemaNode != null) { - if (schemaNode instanceof PrimitiveXNodeImpl) { - PrimitiveXNodeImpl primitiveXNode = (PrimitiveXNodeImpl) schemaNode ; - if (primitiveXNode.isParsed()) { - throw new SchemaException("Cannot convert from PrimitiveXNode to SchemaXNode: node is already parsed: " + primitiveXNode); - } - SchemaXNodeImpl schemaXNode = new SchemaXNodeImpl(); - map.replace(DOMUtil.XSD_SCHEMA_ELEMENT, schemaXNode); - schemaXNode.setSchemaElement(((JsonValueParser) primitiveXNode.getValueParser()).asDomElement()); - } else { - throw new SchemaException("Cannot convert 'schema' field to SchemaXNode: not a PrimitiveNode but " + schemaNode); - } - } - } else if (xnode instanceof ListXNodeImpl) { - for (XNodeImpl item : (ListXNodeImpl) xnode) { - processSchemaNodes(item); - } - } - } - - @NotNull - private XNodeImpl parseValue(JsonParsingContext ctx, IterativeParsingContext ipc) throws IOException, SchemaException { - Validate.notNull(ctx.parser.currentToken()); - - switch (ctx.parser.currentToken()) { - case START_OBJECT: - return parseJsonObject(ctx, ipc); - case START_ARRAY: - return parseToList(ctx, ipc); - case VALUE_STRING: - case VALUE_TRUE: - case VALUE_FALSE: - case VALUE_NUMBER_FLOAT: - case VALUE_NUMBER_INT: - case VALUE_EMBEDDED_OBJECT: // assuming it's a scalar value e.g. !!binary (TODO) - return parseToPrimitive(ctx); - case VALUE_NULL: - return parseToEmptyPrimitive(); - default: - throw new SchemaException("Unexpected current token: " + ctx.parser.currentToken()); - } - } - - /** - * Normally returns a MapXNode. However, there are exceptions: - * - JSON primitives/lists can be simulated by two-member object (@type + @value); in these cases we return respective XNode. - * - Incomplete marker i.e. { "@incomplete" : "true" } should be interpreted as IncompleteMarkerXNode. - */ - @NotNull - private XNodeImpl parseJsonObject(JsonParsingContext ctx, IterativeParsingContext ipc) throws SchemaException, IOException { - Validate.notNull(ctx.parser.currentToken()); - - QName typeName = null; - QNameUtil.QNameInfo elementNameInfo = null; - - Object tid = ctx.parser.getTypeId(); - if (tid != null) { - typeName = tagToTypeName(tid, ctx); - } - - final MapXNodeImpl map = new MapXNodeImpl(); - XNodeImpl wrappedValue = null; - boolean defaultNamespaceDefined = false; - QNameUtil.QNameInfo currentFieldNameInfo = null; - Boolean incomplete = null; - for (;;) { - if (ipc != null && ipc.abortProcessing) { - break; - } - - JsonToken token = ctx.parser.nextToken(); - if (token == null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Unexpected end of data while parsing a map structure at " + getPositionSuffix(ctx)); - break; - } else if (token == JsonToken.END_OBJECT) { - break; - } else if (token == JsonToken.FIELD_NAME) { - String newFieldName = ctx.parser.getCurrentName(); - if (currentFieldNameInfo != null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Two field names in succession: " + currentFieldNameInfo + " and " + newFieldName); - } - currentFieldNameInfo = QNameUtil.uriToQNameInfo(newFieldName, true); - } else { - assert currentFieldNameInfo != null; - PrismContext prismContext = schemaRegistry.getPrismContext(); - // if we look for objects and found an entry different from c:objects - boolean skipValue = false; - if (ipc != null && !isSpecial(currentFieldNameInfo.name) - && (prismContext.getObjectsElementName() == null - || !QNameUtil.match(currentFieldNameInfo.name, prismContext.getObjectsElementName()))) { - if (ipc.dataSent) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Superfluous data after list of objects was found: " + currentFieldNameInfo.name); - skipValue = true; - } else { - ipc = null; - } - } - XNodeImpl valueXNode = parseValue(ctx, ipc); - if (skipValue) { - continue; - } - if (isSpecial(currentFieldNameInfo.name)) { - if (isNamespaceDeclaration(currentFieldNameInfo.name)) { - if (defaultNamespaceDefined) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Default namespace defined more than once at " + getPositionSuffix(ctx)); - } - String namespaceUri = getStringValue(valueXNode, currentFieldNameInfo, ctx); - ctx.defaultNamespaces.put(map, namespaceUri); - defaultNamespaceDefined = true; - if (ipc != null) { - if (ipc.dataSent) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "When parsing list of objects, default namespace was present after the objects " + getPositionSuffix(ctx)); - } else { - ipc.defaultNamespace = namespaceUri; // there is a place for only one @ns declaration (at the level of "objects" map) - } - } - } else if (isTypeDeclaration(currentFieldNameInfo.name)) { - if (typeName != null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Value type defined more than once at " + getPositionSuffix(ctx)); - } - typeName = QNameUtil.uriToQName(getStringValue(valueXNode, currentFieldNameInfo, ctx), true); - } else if (isElementDeclaration(currentFieldNameInfo.name)) { - if (elementNameInfo != null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Element name defined more than once at " + getPositionSuffix(ctx)); - } - elementNameInfo = QNameUtil.uriToQNameInfo(getStringValue(valueXNode, currentFieldNameInfo, ctx), true); - } else if (isValue(currentFieldNameInfo.name)) { - if (wrappedValue != null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Value ('" + PROP_VALUE + "') defined more than once at " + getPositionSuffix(ctx)); - } - wrappedValue = valueXNode; - } else if (isIncompleteDeclaration(currentFieldNameInfo.name)) { - if (incomplete != null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Duplicate @incomplete marker found with the value: " + valueXNode); - } else if (valueXNode instanceof PrimitiveXNodeImpl) { - //noinspection unchecked - Boolean value = ((PrimitiveXNodeImpl) valueXNode) - .getParsedValue(DOMUtil.XSD_BOOLEAN, Boolean.class, ctx.prismParsingContext.getEvaluationMode()); - incomplete = Boolean.TRUE.equals(value); - } else { - ctx.prismParsingContext.warnOrThrow(LOGGER, "@incomplete marker found with incompatible value: " + valueXNode); - } - } - } else { - // Beware of potential unqualified value conflict (see MID-5326). - // Therefore we use special "default-namespace" marker that is dealt with later. - QName key; - if (currentFieldNameInfo.explicitEmptyNamespace || QNameUtil.isQualified(currentFieldNameInfo.name)) { - key = currentFieldNameInfo.name; - } else { - key = new QName(DEFAULT_NAMESPACE_MARKER, currentFieldNameInfo.name.getLocalPart()); - map.setHasDefaultNamespaceMarkers(); - } - map.put(key, valueXNode); - } - currentFieldNameInfo = null; - } - } - // Return either map or primitive value (in case of @type/@value) or incomplete xnode - int haveRegular = !map.isEmpty() ? 1 : 0; - int haveWrapped = wrappedValue != null ? 1 : 0; - int haveIncomplete = Boolean.TRUE.equals(incomplete) ? 1 : 0; - XNodeImpl rv; - if (haveRegular + haveWrapped + haveIncomplete > 1) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "More than one of '" + PROP_VALUE + "', '" + PROP_INCOMPLETE - + "' and regular content present at " + getPositionSuffix(ctx)); - rv = map; - } else { - if (haveIncomplete > 0) { - rv = new IncompleteMarkerXNodeImpl(); - } else if (haveWrapped > 0) { - rv = wrappedValue; - } else { - rv = map; // map can be empty here - } - } - if (typeName != null) { - if (wrappedValue != null && wrappedValue.getTypeQName() != null && !wrappedValue.getTypeQName().equals(typeName)) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Conflicting type names for '" + PROP_VALUE - + "' (" + wrappedValue.getTypeQName() + ") and regular content (" + typeName + ") present at " - + getPositionSuffix(ctx)); - } - rv.setTypeQName(typeName); - rv.setExplicitTypeDeclaration(true); - } - if (elementNameInfo != null) { - if (wrappedValue != null && wrappedValue.getElementName() != null) { - boolean wrappedValueElementNoNamespace = ctx.noNamespaceElementNames.containsKey(wrappedValue); - if (!wrappedValue.getElementName().equals(elementNameInfo.name) - || wrappedValueElementNoNamespace != elementNameInfo.explicitEmptyNamespace) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Conflicting element names for '" + PROP_VALUE - + "' (" + wrappedValue.getElementName() + "; no NS=" + wrappedValueElementNoNamespace - + ") and regular content (" + elementNameInfo.name + "; no NS=" - + elementNameInfo.explicitEmptyNamespace + ") present at " - + getPositionSuffix(ctx)); - } - } - rv.setElementName(elementNameInfo.name); - if (elementNameInfo.explicitEmptyNamespace) { - ctx.noNamespaceElementNames.put(rv, null); - } - } - return rv; - } - - private String getStringValue(XNodeImpl valueXNode, QNameUtil.QNameInfo currentFieldNameInfo, - JsonParsingContext ctx) throws SchemaException { - String stringValue; - if (!(valueXNode instanceof PrimitiveXNodeImpl)) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Value of '" + currentFieldNameInfo + "' attribute must be a primitive one. It is " + valueXNode + " instead. At " + getPositionSuffix(ctx)); - stringValue = ""; - } else { - stringValue = ((PrimitiveXNodeImpl) valueXNode).getStringValue(); - } - return stringValue; - } - - private boolean isSpecial(QName fieldName) { - return isTypeDeclaration(fieldName) - || isIncompleteDeclaration(fieldName) - || isElementDeclaration(fieldName) - || isNamespaceDeclaration(fieldName) - || isValue(fieldName); - } - - private boolean isTypeDeclaration(QName fieldName) { - return PROP_TYPE_QNAME.equals(fieldName); - } - - private boolean isIncompleteDeclaration(QName fieldName) { - return PROP_INCOMPLETE_QNAME.equals(fieldName); - } - - private boolean isElementDeclaration(QName fieldName) { - return PROP_ELEMENT_QNAME.equals(fieldName); - } - - private boolean isNamespaceDeclaration(QName fieldName) { - return PROP_NAMESPACE_QNAME.equals(fieldName); - } - - private boolean isValue(QName fieldName) { - return PROP_VALUE_QNAME.equals(fieldName); - } - - private String getPositionSuffix(JsonParsingContext ctx) { - return String.valueOf(ctx.parser.getCurrentLocation()); - } - - private ListXNodeImpl parseToList(JsonParsingContext ctx, IterativeParsingContext ipc) throws SchemaException, IOException { - Validate.notNull(ctx.parser.currentToken()); - - ListXNodeImpl list = new ListXNodeImpl(); - Object tid = ctx.parser.getTypeId(); - if (tid != null) { - list.setTypeQName(tagToTypeName(tid, ctx)); - } - if (ipc != null) { - ipc.dataSent = true; - } - for (;;) { - JsonToken token = ctx.parser.nextToken(); - if (token == null) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Unexpected end of data while parsing a list structure at " + getPositionSuffix(ctx)); - return list; - } else if (token == JsonToken.END_ARRAY) { - return list; - } else { - if (ipc != null) { - JsonParsingContext childCtx = ctx.createChildContext(); - XNodeImpl value = parseValue(childCtx, null); - List childRoots = valueToRootList(value, ipc.defaultNamespace, childCtx); - for (RootXNodeImpl childRoot : childRoots) { - if (!ipc.handler.handleData(childRoot)) { - ipc.abortProcessing = true; - assert list.isEmpty(); - return list; - } - } - } else { - list.add(parseValue(ctx, null)); - } - } - } - } - - private PrimitiveXNodeImpl parseToPrimitive(JsonParsingContext ctx) throws IOException, SchemaException { - PrimitiveXNodeImpl primitive = new PrimitiveXNodeImpl<>(); - - Object tid = ctx.parser.getTypeId(); - if (tid != null) { - QName typeName = tagToTypeName(tid, ctx); - primitive.setTypeQName(typeName); - primitive.setExplicitTypeDeclaration(true); - } else { - // We don't try to determine XNode type from the implicit JSON/YAML type (integer, number, ...), - // because XNode type prescribes interpretation in midPoint. E.g. YAML string type would be interpreted - // as xsd:string, even if the schema would expect e.g. timestamp. - } - - JsonNode jn = ctx.parser.readValueAs(JsonNode.class); - ValueParser vp = new JsonValueParser<>(ctx.parser, jn); - primitive.setValueParser(vp); - - return primitive; - } - - private PrimitiveXNodeImpl parseToEmptyPrimitive() { - PrimitiveXNodeImpl primitive = new PrimitiveXNodeImpl<>(); - primitive.setValueParser(new JsonNullValueParser<>()); - return primitive; - } - - @SuppressWarnings("unused") // TODO remove if not needed - private QName getCurrentTypeName(JsonParsingContext ctx) throws IOException, SchemaException { - switch (ctx.parser.currentToken()) { - case VALUE_NUMBER_INT: - case VALUE_NUMBER_FLOAT: - return determineNumberType(ctx.parser.getNumberType()); - case VALUE_FALSE: - case VALUE_TRUE: - return DOMUtil.XSD_BOOLEAN; - case VALUE_STRING: - return DOMUtil.XSD_STRING; - case VALUE_NULL: - return null; // TODO? - default: - throw new SchemaException("Unexpected current token type: " + ctx.parser.currentToken() + "/" + ctx.parser.getText() + " at " + getPositionSuffix(ctx)); - } - } - - QName determineNumberType(JsonParser.NumberType numberType) throws SchemaException { - switch (numberType) { - case BIG_DECIMAL: - return DOMUtil.XSD_DECIMAL; - case BIG_INTEGER: - return DOMUtil.XSD_INTEGER; - case LONG: - return DOMUtil.XSD_LONG; - case INT: - return DOMUtil.XSD_INT; - case FLOAT: - return DOMUtil.XSD_FLOAT; - case DOUBLE: - return DOMUtil.XSD_DOUBLE; - default: - throw new SchemaException("Unsupported number type: " + numberType); - } - } - - protected abstract QName tagToTypeName(Object tid, JsonParsingContext ctx) throws IOException, SchemaException; - - private JsonParser configureParser(JsonParser parser) { - ObjectMapper mapper = new ObjectMapper(); - SimpleModule sm = new SimpleModule(); - sm.addDeserializer(QName.class, new QNameDeserializer()); - sm.addDeserializer(UniformItemPath.class, new ItemPathDeserializer()); - sm.addDeserializer(ItemPath.class, new ItemPathDeserializer()); - sm.addDeserializer(PolyString.class, new PolyStringDeserializer()); - sm.addDeserializer(ItemPathType.class, new ItemPathTypeDeserializer()); - - mapper.registerModule(sm); - parser.setCodec(mapper); - return parser; - } - - //endregion - - //region Serialization implementation - - static final class JsonSerializationContext { - @NotNull final JsonGenerator generator; - @NotNull private final SerializationContext prismSerializationContext; - private String currentNamespace; - - private JsonSerializationContext(@NotNull JsonGenerator generator, @Nullable SerializationContext prismSerializationContext) { - this.generator = generator; - this.prismSerializationContext = prismSerializationContext != null ? - prismSerializationContext : - new SerializationContext(null); - } - } - @NotNull - @Override - public String write(@NotNull XNode xnode, @NotNull QName rootElementName, SerializationContext serializationContext) throws SchemaException { - return write(LexicalUtils.createRootXNode((XNodeImpl) xnode, rootElementName), serializationContext); - } - - protected abstract JsonGenerator createJacksonGenerator(StringWriter out) throws SchemaException; - - @NotNull - @Override - public String write(@NotNull RootXNode root, SerializationContext prismSerializationContext) throws SchemaException { - return writeInternal((RootXNodeImpl) root, prismSerializationContext, false); - } - - @NotNull - private String writeInternal(@NotNull XNodeImpl root, SerializationContext prismSerializationContext, boolean useMultiDocument) throws SchemaException { - StringWriter out = new StringWriter(); - try ( JsonGenerator generator = createJacksonGenerator(out) ) { - JsonSerializationContext ctx = new JsonSerializationContext(generator, prismSerializationContext); - if (root instanceof RootXNodeImpl) { - root = ((RootXNodeImpl) root).toMapXNode(); - } - if (root instanceof ListXNodeImpl && useMultiDocument && generator instanceof MidpointYAMLGenerator) { - boolean first = true; - for (XNodeImpl item : ((ListXNodeImpl) root)) { - if (!first) { - ((MidpointYAMLGenerator) generator).newDocument(); - } else { - first = false; - } - serialize(item, ctx, false); - } - } else { - serialize(root, ctx, false); // TODO default namespace - } - } catch (IOException ex) { - throw new SchemaException("Error during serializing to JSON/YAML: " + ex.getMessage(), ex); - } - return out.toString(); - } - - @NotNull - @Override - public String write(@NotNull List roots, QName aggregateElementName, - @Nullable SerializationContext prismSerializationContext) throws SchemaException { - ListXNodeImpl objectsList = new ListXNodeImpl(); - for (RootXNodeImpl root : roots) { - objectsList.add(root.toMapXNode()); - } - XNodeImpl aggregate; - if (aggregateElementName != null) { - aggregate = new RootXNodeImpl(aggregateElementName, objectsList); - return writeInternal(aggregate, prismSerializationContext, false); - } else { - aggregate = objectsList; - return writeInternal(aggregate, prismSerializationContext, true); - } - } - - private void serialize(XNodeImpl xnode, JsonSerializationContext ctx, boolean inValueWrapMode) throws IOException { - if (xnode instanceof MapXNodeImpl) { - serializeFromMap((MapXNodeImpl) xnode, ctx); - } else if (xnode == null) { - serializeFromNull(ctx); - } else if (needsValueWrapping(xnode) && !inValueWrapMode) { - ctx.generator.writeStartObject(); - resetInlineTypeIfPossible(ctx); - writeAuxiliaryInformation(xnode, ctx); - ctx.generator.writeFieldName(PROP_VALUE); - serialize(xnode, ctx, true); - ctx.generator.writeEndObject(); - } else if (xnode instanceof ListXNodeImpl) { - serializeFromList((ListXNodeImpl) xnode, ctx); - } else if (xnode instanceof PrimitiveXNodeImpl) { - serializeFromPrimitive((PrimitiveXNodeImpl) xnode, ctx); - } else if (xnode instanceof SchemaXNodeImpl) { - serializeFromSchema((SchemaXNodeImpl) xnode, ctx); - } else if (xnode instanceof IncompleteMarkerXNodeImpl) { - ctx.generator.writeStartObject(); - ctx.generator.writeFieldName(PROP_INCOMPLETE); - ctx.generator.writeBoolean(true); - ctx.generator.writeEndObject(); - } else { - throw new UnsupportedOperationException("Cannot serialize from " + xnode); - } - } - - private void serializeFromNull(JsonSerializationContext ctx) throws IOException { - ctx.generator.writeNull(); - } - - private void writeAuxiliaryInformation(XNodeImpl xnode, JsonSerializationContext ctx) throws IOException { - QName elementName = xnode.getElementName(); - if (elementName != null) { - ctx.generator.writeObjectField(PROP_ELEMENT, createElementNameUri(elementName, ctx)); - } - QName typeName = getExplicitType(xnode); - if (typeName != null) { - if (!supportsInlineTypes()) { - ctx.generator.writeObjectField(PROP_TYPE, typeName); - } - } - } - - private boolean needsValueWrapping(XNodeImpl xnode) { - return xnode.getElementName() != null - || getExplicitType(xnode) != null && !supportsInlineTypes(); - } - - protected abstract boolean supportsInlineTypes(); - - protected abstract void writeInlineType(QName typeName, JsonSerializationContext ctx) throws IOException; - - private void serializeFromMap(MapXNodeImpl map, JsonSerializationContext ctx) throws IOException { - writeInlineTypeIfNeeded(map, ctx); - ctx.generator.writeStartObject(); - resetInlineTypeIfPossible(ctx); - String oldDefaultNamespace = ctx.currentNamespace; - generateNsDeclarationIfNeeded(map, ctx); - writeAuxiliaryInformation(map, ctx); - for (Entry entry : map.entrySet()) { - if (entry.getValue() == null) { - continue; - } - ctx.generator.writeFieldName(createKeyUri(entry, ctx)); - serialize(entry.getValue(), ctx, false); - } - ctx.generator.writeEndObject(); - ctx.currentNamespace = oldDefaultNamespace; - } - - protected void resetInlineTypeIfPossible(JsonSerializationContext ctx) { - } - - private void generateNsDeclarationIfNeeded(MapXNodeImpl map, JsonSerializationContext ctx) throws IOException { - SerializationOptions opts = ctx.prismSerializationContext.getOptions(); - if (!SerializationOptions.isUseNsProperty(opts) || map.isEmpty()) { - return; - } - String namespace = determineNewCurrentNamespace(map, ctx); - if (namespace != null && !StringUtils.equals(namespace, ctx.currentNamespace)) { - ctx.currentNamespace = namespace; - ctx.generator.writeFieldName(PROP_NAMESPACE); - ctx.generator.writeString(namespace); - } - } - - private String determineNewCurrentNamespace(MapXNodeImpl map, JsonSerializationContext ctx) { - Map counts = new HashMap<>(); - for (QName childName : map.keySet()) { - String childNs = childName.getNamespaceURI(); - if (StringUtils.isEmpty(childNs)) { - continue; - } - if (childNs.equals(ctx.currentNamespace)) { - return ctx.currentNamespace; // found existing => continue with it - } - increaseCounter(counts, childNs); - } - if (map.getElementName() != null && QNameUtil.hasNamespace(map.getElementName())) { - increaseCounter(counts, map.getElementName().getNamespaceURI()); - } - // otherwise, take the URI that occurs the most in the map - Entry max = null; - for (Entry count : counts.entrySet()) { - if (max == null || count.getValue() > max.getValue()) { - max = count; - } - } - return max != null ? max.getKey() : null; - } - - private void increaseCounter(Map counts, String childNs) { - Integer c = counts.get(childNs); - counts.put(childNs, c != null ? c+1 : 1); - } - - private String createKeyUri(Entry entry, JsonSerializationContext ctx) { - QName key = entry.getKey(); - if (namespaceMatch(ctx.currentNamespace, key.getNamespaceURI())) { - return key.getLocalPart(); - } else if (StringUtils.isNotEmpty(ctx.currentNamespace) && !isAttribute(entry.getValue())) { - return QNameUtil.qNameToUri(key, true); // items with no namespace should be written as such (starting with '#') - } else { - return QNameUtil.qNameToUri(key, false); // items with no namespace can be written in plain - } - } - - private String createElementNameUri(QName elementName, JsonSerializationContext ctx) { - if (namespaceMatch(ctx.currentNamespace, elementName.getNamespaceURI())) { - return elementName.getLocalPart(); - } else { - return QNameUtil.qNameToUri(elementName, StringUtils.isNotEmpty(ctx.currentNamespace)); - } - } - - private boolean isAttribute(XNodeImpl node) { - return node instanceof PrimitiveXNodeImpl && ((PrimitiveXNodeImpl) node).isAttribute(); - } - - private boolean namespaceMatch(String currentNamespace, String itemNamespace) { - if (StringUtils.isEmpty(currentNamespace)) { - return StringUtils.isEmpty(itemNamespace); - } else { - return currentNamespace.equals(itemNamespace); - } - } - - private void serializeFromList(ListXNodeImpl list, JsonSerializationContext ctx) throws IOException { - writeInlineTypeIfNeeded(list, ctx); - ctx.generator.writeStartArray(); - resetInlineTypeIfPossible(ctx); - for (XNodeImpl item : list) { - serialize(item, ctx, false); - } - ctx.generator.writeEndArray(); - } - - private void writeInlineTypeIfNeeded(XNodeImpl node, JsonSerializationContext ctx) throws IOException { - QName explicitType = getExplicitType(node); - if (supportsInlineTypes() && explicitType != null) { - writeInlineType(explicitType, ctx); - } - } - - private void serializeFromSchema(SchemaXNodeImpl node, JsonSerializationContext ctx) throws IOException { - writeInlineTypeIfNeeded(node, ctx); - Element schemaElement = node.getSchemaElement(); - DOMUtil.fixNamespaceDeclarations(schemaElement); // TODO reconsider if it's OK to modify schema DOM element in this way - ctx.generator.writeObject(schemaElement); - } - - - private void serializeFromPrimitive(PrimitiveXNodeImpl primitive, JsonSerializationContext ctx) throws IOException { - writeInlineTypeIfNeeded(primitive, ctx); - if (primitive.isParsed()) { - ctx.generator.writeObject(primitive.getValue()); - } else { - ctx.generator.writeObject(primitive.getStringValue()); - } - } - - private QName getExplicitType(XNodeImpl xnode) { - return xnode.isExplicitTypeDeclaration() ? xnode.getTypeQName() : null; - } - - @SuppressWarnings("unused") // TODO - private String serializeNsIfNeeded(QName subNodeName, String globalNamespace, JsonGenerator generator) throws IOException { - if (subNodeName == null){ - return globalNamespace; - } - String subNodeNs = subNodeName.getNamespaceURI(); - if (StringUtils.isNotBlank(subNodeNs)){ - if (!subNodeNs.equals(globalNamespace)){ - globalNamespace = subNodeNs; - generator.writeStringField(PROP_NAMESPACE, globalNamespace); - - } - } - return globalNamespace; - } - //endregion - -} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/Constants.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/Constants.java new file mode 100644 index 00000000000..bb71cf7a266 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/Constants.java @@ -0,0 +1,28 @@ +/* + * 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.midpoint.prism.impl.lex.json; + +import javax.xml.namespace.QName; + +/** + * TODO + */ +public class Constants { + public static final String PROP_NAMESPACE = "@ns"; + public static final QName PROP_NAMESPACE_QNAME = new QName(PROP_NAMESPACE); + public static final String PROP_TYPE = "@type"; + public static final QName PROP_TYPE_QNAME = new QName(PROP_TYPE); + public static final String PROP_INCOMPLETE = "@incomplete"; + public static final QName PROP_INCOMPLETE_QNAME = new QName(PROP_INCOMPLETE); + public static final String PROP_ELEMENT = "@element"; + public static final QName PROP_ELEMENT_QNAME = new QName(PROP_ELEMENT); + public static final String PROP_VALUE = "@value"; + public static final QName PROP_VALUE_QNAME = new QName(PROP_VALUE); + public static final String PROP_METADATA = "@metadata"; + public static final QName PROP_METADATA_QNAME = new QName(PROP_METADATA); +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/DelegatingLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/DelegatingLexicalProcessor.java new file mode 100644 index 00000000000..0b1343db530 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/DelegatingLexicalProcessor.java @@ -0,0 +1,87 @@ +/* + * 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.prism.impl.lex.json; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.prism.ParserSource; +import com.evolveum.midpoint.prism.ParsingContext; +import com.evolveum.midpoint.prism.SerializationContext; +import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; + +import com.evolveum.midpoint.prism.impl.lex.json.reader.AbstractReader; +import com.evolveum.midpoint.prism.impl.lex.json.writer.AbstractWriter; +import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; +import com.evolveum.midpoint.prism.xnode.RootXNode; +import com.evolveum.midpoint.prism.xnode.XNode; +import com.evolveum.midpoint.util.exception.SchemaException; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class DelegatingLexicalProcessor implements LexicalProcessor { + + @NotNull private final AbstractReader reader; + @NotNull private final AbstractWriter writer; + + public DelegatingLexicalProcessor(@NotNull AbstractReader reader, @NotNull AbstractWriter writer) { + this.reader = reader; + this.writer = writer; + } + + @Override + public @NotNull RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) + throws SchemaException, IOException { + return reader.read(source, parsingContext); + } + + @Override + public @NotNull List readObjects(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) + throws SchemaException, IOException { + return reader.readObjects(source, parsingContext); + } + + @Override + public void readObjectsIteratively(@NotNull ParserSource source, @NotNull ParsingContext parsingContext, + RootXNodeHandler handler) throws SchemaException, IOException { + reader.readObjectsIteratively(source, parsingContext, handler); + } + + @Override + public boolean canRead(@NotNull File file) throws IOException { + return reader.canRead(file); + } + + @Override + public boolean canRead(@NotNull String dataString) { + return reader.canRead(dataString); + } + + @NotNull + @Override + public String write(@NotNull RootXNode xnode, @Nullable SerializationContext serializationContext) throws SchemaException { + return writer.write(xnode, serializationContext); + } + + @NotNull + @Override + public String write(@NotNull XNode xnode, + @NotNull QName rootElementName, @Nullable SerializationContext serializationContext) throws SchemaException { + return writer.write(xnode, rootElementName, serializationContext); + } + + @NotNull + @Override + public String write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException { + return writer.write(roots, context); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonValueParser.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonValueParser.java index 4c6b1be9bd3..4e4e9a18100 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonValueParser.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonValueParser.java @@ -30,9 +30,9 @@ public class JsonValueParser implements ValueParser { @NotNull private final JsonParser parser; - private JsonNode node; + private final JsonNode node; - JsonValueParser(@NotNull JsonParser parser, JsonNode node) { + public JsonValueParser(@NotNull JsonParser parser, JsonNode node) { this.parser = parser; this.node = node; } @@ -89,7 +89,7 @@ public ValueParser freeze() { return this; // TODO implement } - Element asDomElement() throws IOException { + public Element asDomElement() throws IOException { ObjectMapper mapper = (ObjectMapper) parser.getCodec(); ObjectReader r = mapper.readerFor(Document.class); return ((Document) r.readValue(node)).getDocumentElement(); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/NullLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/NullLexicalProcessor.java index dd74d309576..3141dd6090a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/NullLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/NullLexicalProcessor.java @@ -22,17 +22,16 @@ import javax.xml.namespace.QName; import java.io.File; -import java.io.IOException; import java.util.List; /** - * @author mederly + * "Null" processor that reads XNodes into XNodes and writes XNodes as XNodes. */ public class NullLexicalProcessor implements LexicalProcessor { @NotNull @Override - public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { + public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) { if (!(source instanceof ParserXNodeSource)) { throw new IllegalStateException("Unsupported parser source: " + source.getClass().getName()); } @@ -41,18 +40,18 @@ public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext @NotNull @Override - public List readObjects(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { + public List readObjects(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) { throw new UnsupportedOperationException(); } @Override public void readObjectsIteratively(@NotNull ParserSource source, @NotNull ParsingContext parsingContext, - RootXNodeHandler handler) throws SchemaException, IOException { + RootXNodeHandler handler) { throw new UnsupportedOperationException(); } @Override - public boolean canRead(@NotNull File file) throws IOException { + public boolean canRead(@NotNull File file) { return false; } @@ -76,7 +75,7 @@ public XNodeImpl write(@NotNull XNode xnode, @NotNull QName rootElementName, @Nu @NotNull @Override - public XNodeImpl write(@NotNull List roots, @Nullable QName aggregateElementName, + public XNodeImpl write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException { throw new UnsupportedOperationException("NullLexicalProcessor.write is not supported for a collection of objects"); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/YamlLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/YamlLexicalProcessor.java deleted file mode 100644 index 02c5da5f1f4..00000000000 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/YamlLexicalProcessor.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2010-2017 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.prism.impl.lex.json; - -import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLFactory; -import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLGenerator; -import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLParser; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.fasterxml.jackson.core.Version; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; -import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Element; - -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.namespace.QName; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -//import com.fasterxml.jackson.core.YAMLGenerator; - -public class YamlLexicalProcessor extends AbstractJsonLexicalProcessor { - - private static final String YAML = "tag:yaml.org,2002:"; - private static final String TAG_STRING = YAML + "str"; - private static final String TAG_INT = YAML + "int"; - private static final String TAG_BOOL = YAML + "bool"; - private static final String TAG_FLOAT = YAML + "float"; - private static final String TAG_BINARY = YAML + "binary"; // base64-encoded string - private static final String TAG_NULL = YAML + "null"; - - public YamlLexicalProcessor(@NotNull SchemaRegistry schemaRegistry) { - super(schemaRegistry); - } - - @Override - public boolean canRead(@NotNull File file) throws IOException { - return file.getName().endsWith(".yaml"); - } - - @Override - public boolean canRead(@NotNull String dataString) { - return dataString.startsWith("---"); - } - - public YAMLGenerator createJacksonGenerator(StringWriter out) throws SchemaException{ - try { - MidpointYAMLFactory factory = new MidpointYAMLFactory(); - MidpointYAMLGenerator generator = (MidpointYAMLGenerator) factory.createGenerator(out); - generator.setPrettyPrinter(new DefaultPrettyPrinter()); - generator.setCodec(configureMapperForSerialization()); - return generator; - } catch (IOException ex){ - throw new SchemaException("Schema error during serializing to JSON.", ex); - } - } - - private ObjectMapper configureMapperForSerialization(){ - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); -// mapper.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS, As.EXISTING_PROPERTY); -// mapper.configure(SerializationFeaCture.); -// mapper.setSerializationInclusion(Include.NON_NULL); - mapper.registerModule(createSerializerModule()); - - mapper.disableDefaultTyping(); - -// mapper.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS); -// mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.EXISTING_PROPERTY); -// //mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.EXTERNAL_PROPERTY); -// mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); - - return mapper; - } - - private Module createSerializerModule(){ - SimpleModule module = new SimpleModule("MidpointModule", new Version(0, 0, 0, "aa")); - module.addSerializer(QName.class, new QNameSerializer()); - module.addSerializer(PolyString.class, new PolyStringSerializer()); - module.addSerializer(ItemPath.class, new ItemPathSerializer()); - module.addSerializer(ItemPathType.class, new ItemPathTypeSerializer()); -// module.addSerializer(JAXBElement.class, new JaxbElementSerializer()); - module.addSerializer(XMLGregorianCalendar.class, new XmlGregorianCalendarSerializer()); - module.addSerializer(Element.class, new DomElementSerializer()); - return module; - } - - @Override - protected MidpointYAMLParser createJacksonParser(InputStream stream) throws SchemaException, IOException { - MidpointYAMLFactory factory = new MidpointYAMLFactory(); - try { - MidpointYAMLParser p = (MidpointYAMLParser) factory.createParser(stream); -// p.enable(Feature.BOGUS); -// String oid = p.getObjectId(); - return p; - } catch (IOException e) { - throw e; - } - } - - @Override - protected QName tagToTypeName(Object tag, AbstractJsonLexicalProcessor.JsonParsingContext ctx) throws IOException, SchemaException { - if (tag == null) { - return null; - } if (TAG_BINARY.equals(tag)) { - return DOMUtil.XSD_BASE64BINARY; - } if (TAG_STRING.equals(tag)) { - return DOMUtil.XSD_STRING; - } else if (TAG_BOOL.equals(tag)) { - return DOMUtil.XSD_BOOLEAN; - } else if (TAG_NULL.equals(tag)) { - return null; // ??? - } else if (TAG_INT.equals(tag)) { - QName type = determineNumberType(ctx.parser.getNumberType()); - if (DOMUtil.XSD_INT.equals(type) || DOMUtil.XSD_INTEGER.equals(type)) { - return type; - } else { - return DOMUtil.XSD_INT; // suspicious - } - } else if (TAG_FLOAT.equals(tag)) { - QName type = determineNumberType(ctx.parser.getNumberType()); - if (DOMUtil.XSD_FLOAT.equals(type) || DOMUtil.XSD_DOUBLE.equals(type) || DOMUtil.XSD_DECIMAL.equals(type)) { - return type; - } else { - return DOMUtil.XSD_FLOAT; // suspicious - } - } else if (tag instanceof String) { - return QNameUtil.uriToQName((String) tag, true); - } else { - // TODO issue a warning? - return null; - } - } - - @Override - protected boolean supportsInlineTypes() { - return true; - } - - @Override - protected void writeInlineType(QName typeName, AbstractJsonLexicalProcessor.JsonSerializationContext ctx) throws IOException { - ctx.generator.writeTypeId(QNameUtil.qNameToUri(typeName, false, '/')); - } - - @Override - protected void resetInlineTypeIfPossible(JsonSerializationContext ctx) { - ((MidpointYAMLGenerator) ctx.generator).resetTypeId(); // brutal hack - } -} - - diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/AbstractReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/AbstractReader.java new file mode 100644 index 00000000000..01dd2ffef44 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/AbstractReader.java @@ -0,0 +1,174 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import com.evolveum.midpoint.prism.ParserSource; +import com.evolveum.midpoint.prism.ParsingContext; +import com.evolveum.midpoint.prism.impl.ParsingContextImpl; +import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; +import com.evolveum.midpoint.prism.impl.xnode.*; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.path.UniformItemPath; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.util.logging.LoggingUtils; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.jetbrains.annotations.NotNull; + +import javax.xml.namespace.QName; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Takes care of reading JSON/YAML to XNode. + */ +public abstract class AbstractReader { + + private static final Trace LOGGER = TraceManager.getTrace(AbstractReader.class); + + @NotNull protected final SchemaRegistry schemaRegistry; + + AbstractReader(@NotNull SchemaRegistry schemaRegistry) { + this.schemaRegistry = schemaRegistry; + } + + @NotNull + public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { + List nodes = readInternal(source, parsingContext, false); + if (nodes.isEmpty()) { + throw new SchemaException("No data at input"); + } else if (nodes.size() > 1) { + throw new SchemaException("More than one object found: " + nodes); // should not occur + } else { + return nodes.get(0); + } + } + + /** + * Honors multi-document files and multiple objects in a single document (list-as-root mechanisms). + */ + @NotNull + public List readObjects(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { + return readInternal(source, parsingContext, true); + } + + @NotNull + private List readInternal(@NotNull ParserSource source, @NotNull ParsingContext parsingContext, + boolean expectingMultipleObjects) throws SchemaException, IOException { + InputStream is = source.getInputStream(); + try { + JsonParser parser = createJacksonParser(is); + List rv = new ArrayList<>(); + readFromStart(parser, parsingContext, rv::add, expectingMultipleObjects); + return rv; + } finally { + if (source.closeStreamAfterParsing()) { + closeQuietly(is); + } + } + } + + private void closeQuietly(InputStream is) { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + LoggingUtils.logExceptionAsWarning(LOGGER, "Couldn't close the input stream", e); + } + } + } + + public void readObjectsIteratively(@NotNull ParserSource source, @NotNull ParsingContext parsingContext, + LexicalProcessor.RootXNodeHandler handler) throws SchemaException, IOException { + InputStream is = source.getInputStream(); + try { + JsonParser parser = createJacksonParser(is); + readFromStart(parser, parsingContext, handler, true); + } finally { + if (source.closeStreamAfterParsing()) { + closeQuietly(is); + } + } + } + + protected abstract JsonParser createJacksonParser(InputStream stream) throws SchemaException, IOException; + + @FunctionalInterface + interface YamlTagResolver { + QName tagToTypeName(Object tid, JsonReadingContext ctx) throws IOException, SchemaException; + } + + private void readFromStart(JsonParser unconfiguredParser, ParsingContext parsingContext, + LexicalProcessor.RootXNodeHandler handler, boolean expectingMultipleObjects) throws SchemaException, IOException { + JsonParser configuredParser = configureParser(unconfiguredParser); + JsonReadingContext ctx = new JsonReadingContext(configuredParser, (ParsingContextImpl) parsingContext, + handler, this::tagToTypeName, schemaRegistry.getPrismContext()); + readTreatingExceptions(expectingMultipleObjects, configuredParser, ctx); + } + + private void readTreatingExceptions(boolean expectingMultipleObjects, JsonParser configuredParser, JsonReadingContext ctx) + throws SchemaException, IOException { + try { + readFirstTokenAndCheckEmptyInput(configuredParser); + if (supportsMultipleDocuments()) { + new MultiDocumentReader(ctx).read(expectingMultipleObjects); + } else { + new DocumentReader(ctx).read(expectingMultipleObjects); + } + } catch (SchemaException e) { + throw e; + } catch (JsonParseException e) { + throw new SchemaException("Couldn't parse JSON/YAML object: " + e.getMessage() + ctx.getPositionSuffixIfPresent(), e); + } catch (IOException e) { + throw new IOException("Couldn't parse JSON/YAML object: " + e.getMessage() + ctx.getPositionSuffixIfPresent(), e); + } catch (Throwable t) { + throw new SystemException("Couldn't parse JSON/YAML object: " + t.getMessage() + ctx.getPositionSuffixIfPresent(), t); + } + } + + abstract boolean supportsMultipleDocuments(); + + private void readFirstTokenAndCheckEmptyInput(JsonParser configuredParser) throws IOException, SchemaException { + configuredParser.nextToken(); + if (configuredParser.currentToken() == null) { + throw new SchemaException("Nothing to parse: the input is empty."); + } + } + + private JsonParser configureParser(JsonParser parser) { + ObjectMapper mapper = new ObjectMapper(); + SimpleModule sm = new SimpleModule(); + sm.addDeserializer(QName.class, new QNameDeserializer()); + sm.addDeserializer(UniformItemPath.class, new ItemPathDeserializer()); + sm.addDeserializer(ItemPath.class, new ItemPathDeserializer()); + sm.addDeserializer(PolyString.class, new PolyStringDeserializer()); + sm.addDeserializer(ItemPathType.class, new ItemPathTypeDeserializer()); + + mapper.registerModule(sm); + parser.setCodec(mapper); + return parser; + } + + protected abstract QName tagToTypeName(Object tid, JsonReadingContext ctx) throws IOException, SchemaException; + + public abstract boolean canRead(@NotNull File file) throws IOException; + + public abstract boolean canRead(@NotNull String dataString); +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/DocumentReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/DocumentReader.java new file mode 100644 index 00000000000..39655997175 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/DocumentReader.java @@ -0,0 +1,45 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonToken; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * Reads a single JSON/YAML document. + * It contains either a single RootXNode or a list of RootXNodes. + * ("c:objects" method of storing multiple objects is no longer supported) + */ +class DocumentReader { + + @NotNull private final JsonReadingContext ctx; + + DocumentReader(@NotNull JsonReadingContext ctx) { + this.ctx = ctx; + } + + void read(boolean expectingMultipleObjects) throws IOException, SchemaException { + if (expectingMultipleObjects && ctx.parser.getCurrentToken() == JsonToken.START_ARRAY) { + ctx.parser.nextToken(); + while (!ctx.isAborted() && ctx.parser.getCurrentToken() != JsonToken.END_ARRAY) { + read(); + ctx.parser.nextToken(); // END_OBJECT to START_OBJECT or END_ARRAY + } + } else { + read(); + } + } + + private void read() throws IOException, SchemaException { + new RootObjectReader(ctx).read(); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathDeserializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/ItemPathDeserializer.java similarity index 92% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathDeserializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/ItemPathDeserializer.java index f28d45e206d..396c3273e12 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathDeserializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/ItemPathDeserializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.reader; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathTypeDeserializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/ItemPathTypeDeserializer.java similarity index 92% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathTypeDeserializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/ItemPathTypeDeserializer.java index 1c602115126..861d6f10c7c 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathTypeDeserializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/ItemPathTypeDeserializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.reader; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonObjectTokenReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonObjectTokenReader.java new file mode 100644 index 00000000000..047b7deeaa4 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonObjectTokenReader.java @@ -0,0 +1,343 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import static com.evolveum.midpoint.prism.impl.lex.json.reader.RootObjectReader.DEFAULT_NAMESPACE_MARKER; + +import java.io.IOException; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.prism.xnode.MapXNode; + +import com.evolveum.midpoint.prism.xnode.MetadataAware; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.impl.lex.json.Constants; +import com.evolveum.midpoint.prism.impl.xnode.IncompleteMarkerXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.evolveum.midpoint.prism.marshaller.XNodeProcessorEvaluationMode; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; + +/** + * Reads JSON/YAML objects. This is the most complex part of the reading process. + */ +class JsonObjectTokenReader { + + private static final Trace LOGGER = TraceManager.getTrace(RootObjectReader.class); + + @NotNull private final JsonParser parser; + @NotNull private final JsonReadingContext ctx; + + /** + * Map corresponding to the current object. + * It might or might not be used as a return value - depending on circumstances. + */ + @NotNull private final MapXNodeImpl map = new MapXNodeImpl(); + + /** + * Name of the type of this XNode. + * Derived from YAML tag or from @type declaration. + * Should be set only once. + */ + private QName typeName; + + /** + * Name of the element for this XNode. (From @element declaration.) + * Should be set only once. + */ + private QNameUtil.QNameInfo elementName; + + /** + * Wrapped value (@value declaration). + * Should be set only once. + */ + private XNodeImpl wrappedValue; + + /** + * Metadata (@metadata). + */ + private MapXNode metadata; + + /** + * Value of the "incomplete" flag (@incomplete). + * Should be set only once. + */ + private Boolean incomplete; + + /** + * Namespace (@ns). + * Should be set only once. + */ + private String declaredNamespace; + + /** + * Name of the currently processed field. + * Overwritten many times. + */ + private QNameUtil.QNameInfo currentFieldName; + + /** + * Value of the currently processed field. + * Overwritten many times. + */ + private XNodeImpl currentFieldValue; + + JsonObjectTokenReader(@NotNull JsonReadingContext ctx) { + this.ctx = ctx; + this.parser = ctx.parser; + } + + /** + * Normally returns a MapXNode. However, there are exceptions: + * - JSON primitives/lists can be simulated by two-member object (@type + @value); in these cases we return respective XNode. + * - Incomplete marker i.e. { "@incomplete" : "true" } should be interpreted as IncompleteMarkerXNode. + */ + @NotNull XNodeImpl read() throws IOException, SchemaException { + processYamlTag(); + processFields(); + return postProcess(); + } + + private void processYamlTag() throws IOException, SchemaException { + Object typeId = parser.getTypeId(); + if (typeId != null) { + typeName = ctx.yamlTagResolver.tagToTypeName(typeId, ctx); + } + } + + private void processFields() throws IOException, SchemaException { + while (!ctx.isAborted()) { + JsonToken token = parser.nextToken(); + if (token == null) { + warnOrThrow("Unexpected end of data while parsing a map structure"); + ctx.setAborted(); + break; + } else if (token == JsonToken.END_OBJECT) { + break; + } else if (token == JsonToken.FIELD_NAME) { + processFieldName(); + } else { + processFieldValue(); + } + } + } + + private void processFieldName() throws IOException, SchemaException { + String newFieldName = parser.getCurrentName(); + if (currentFieldName != null) { + warnOrThrow("Two field names in succession: " + currentFieldName + " and " + newFieldName); + } + currentFieldName = QNameUtil.uriToQNameInfo(newFieldName, true); + } + + private void processFieldValue() throws IOException, SchemaException { + assert currentFieldName != null; + currentFieldValue = readValue(); + if (isNamespaceDeclaration()) { + processNamespaceDeclaration(); + } else if (isTypeDeclaration()) { + processTypeDeclaration(); + } else if (isElementDeclaration()) { + processElementNameDeclaration(); + } else if (isWrappedValue()) { + processWrappedValue(); + } else if (isMetadataValue()) { + processMetadataValue(); + } else if (isIncompleteDeclaration()) { + processIncompleteDeclaration(); + } else { + processStandardFieldValue(); + } + currentFieldName = null; + currentFieldValue = null; + } + + private XNodeImpl readValue() throws IOException, SchemaException { + return new JsonOtherTokenReader(ctx).readValue(); + } + + private void processStandardFieldValue() { + // Beware of potential unqualified value conflict (see MID-5326). + // Therefore we use special "default-namespace" marker that is dealt with later. + QName key; + if (currentFieldName.explicitEmptyNamespace || QNameUtil.isQualified(currentFieldName.name)) { + key = currentFieldName.name; + } else { + key = new QName(DEFAULT_NAMESPACE_MARKER, currentFieldName.name.getLocalPart()); + map.setHasDefaultNamespaceMarkers(); + } + map.put(key, currentFieldValue); + } + + private void processIncompleteDeclaration() throws SchemaException { + if (incomplete != null) { + warnOrThrow("Duplicate @incomplete marker found with the value: " + currentFieldValue); + } else if (currentFieldValue instanceof PrimitiveXNodeImpl) { + //noinspection unchecked + Boolean realValue = ((PrimitiveXNodeImpl) currentFieldValue) + .getParsedValue(DOMUtil.XSD_BOOLEAN, Boolean.class, getEvaluationMode()); + incomplete = Boolean.TRUE.equals(realValue); + } else { + warnOrThrow("@incomplete marker found with incompatible value: " + currentFieldValue); + } + } + + private void processWrappedValue() throws SchemaException { + if (wrappedValue != null) { + warnOrThrow("Value ('" + Constants.PROP_VALUE + "') defined more than once"); + } + wrappedValue = currentFieldValue; + } + + private void processMetadataValue() throws SchemaException { + if (metadata != null) { + warnOrThrow("Value ('" + Constants.PROP_METADATA + "') defined more than once"); + } + if (currentFieldValue instanceof MapXNode) { + metadata = (MapXNode) currentFieldValue; + } else { + warnOrThrow("Metadata is not a map XNode: " + currentFieldValue.debugDump()); + } + } + + private void processElementNameDeclaration() throws SchemaException { + if (elementName != null) { + warnOrThrow("Element name defined more than once"); + } + elementName = QNameUtil.uriToQNameInfo(getCurrentFieldStringValue(), true); + } + + private void processTypeDeclaration() throws SchemaException { + if (typeName != null) { + warnOrThrow("Value type defined more than once"); + } + typeName = QNameUtil.uriToQName(getCurrentFieldStringValue(), true); + } + + private void processNamespaceDeclaration() throws SchemaException { + if (declaredNamespace != null) { + warnOrThrow("Default namespace defined more than once"); + } + declaredNamespace = getCurrentFieldStringValue(); + } + + @NotNull + private XNodeImpl postProcess() throws SchemaException { + // Return either map or primitive value (in case of @type/@value) or incomplete xnode + int haveRegular = !map.isEmpty() ? 1 : 0; + int haveWrapped = wrappedValue != null ? 1 : 0; + int haveIncomplete = Boolean.TRUE.equals(incomplete) ? 1 : 0; + XNodeImpl rv; + if (haveRegular + haveWrapped + haveIncomplete > 1) { + warnOrThrow("More than one of '" + Constants.PROP_VALUE + "', '" + Constants.PROP_INCOMPLETE + + "' and regular content present"); + rv = map; + } else { + if (haveIncomplete > 0) { + rv = new IncompleteMarkerXNodeImpl(); + } else if (haveWrapped > 0) { + rv = wrappedValue; + } else { + rv = map; // map can be empty here + } + } + if (typeName != null) { + if (wrappedValue != null && wrappedValue.getTypeQName() != null && !wrappedValue.getTypeQName().equals(typeName)) { + warnOrThrow("Conflicting type names for '" + Constants.PROP_VALUE + + "' (" + wrappedValue.getTypeQName() + ") and regular content (" + typeName + ") present"); + } + rv.setTypeQName(typeName); + rv.setExplicitTypeDeclaration(true); + } + if (elementName != null) { + if (wrappedValue != null && wrappedValue.getElementName() != null) { + boolean wrappedValueElementNoNamespace = ctx.noNamespaceElementNames.containsKey(wrappedValue); + if (!wrappedValue.getElementName().equals(elementName.name) + || wrappedValueElementNoNamespace != elementName.explicitEmptyNamespace) { + warnOrThrow("Conflicting element names for '" + Constants.PROP_VALUE + + "' (" + wrappedValue.getElementName() + "; no NS=" + wrappedValueElementNoNamespace + + ") and regular content (" + elementName.name + "; no NS=" + + elementName.explicitEmptyNamespace + ") present"); + } + } + rv.setElementName(elementName.name); + if (elementName.explicitEmptyNamespace) { + ctx.noNamespaceElementNames.put(rv, null); + } + } + + if (declaredNamespace != null) { + if (rv instanceof MapXNodeImpl) { + ctx.defaultNamespaces.put((MapXNodeImpl) rv, declaredNamespace); + } + if (metadata != null) { + ctx.defaultNamespaces.put((MapXNodeImpl) metadata, declaredNamespace); + } + } + + if (metadata != null) { + if (rv instanceof MetadataAware) { + ((MetadataAware) rv).setMetadataNode(metadata); + } else { + warnOrThrow("Couldn't apply metadata to non-metadata-aware node: " + rv.getClass()); + } + } + + return rv; + } + + private String getCurrentFieldStringValue() throws SchemaException { + if (currentFieldValue instanceof PrimitiveXNodeImpl) { + return ((PrimitiveXNodeImpl) currentFieldValue).getStringValue(); + } else { + warnOrThrow("Value of '" + currentFieldName + "' attribute must be a primitive one. It is " + currentFieldValue + " instead"); + return ""; + } + } + + private XNodeProcessorEvaluationMode getEvaluationMode() { + return ctx.prismParsingContext.getEvaluationMode(); + } + + private boolean isTypeDeclaration() { + return Constants.PROP_TYPE_QNAME.equals(currentFieldName.name); + } + + private boolean isIncompleteDeclaration() { + return Constants.PROP_INCOMPLETE_QNAME.equals(currentFieldName.name); + } + + private boolean isElementDeclaration() { + return Constants.PROP_ELEMENT_QNAME.equals(currentFieldName.name); + } + + private boolean isNamespaceDeclaration() { + return Constants.PROP_NAMESPACE_QNAME.equals(currentFieldName.name); + } + + private boolean isWrappedValue() { + return Constants.PROP_VALUE_QNAME.equals(currentFieldName.name); + } + + private boolean isMetadataValue() { + return Constants.PROP_METADATA_QNAME.equals(currentFieldName.name); + } + + private void warnOrThrow(String message) throws SchemaException { + ctx.prismParsingContext.warnOrThrow(LOGGER, message + ". At " + ctx.getPositionSuffix()); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonOtherTokenReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonOtherTokenReader.java new file mode 100644 index 00000000000..3eaeef7f96c --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonOtherTokenReader.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.midpoint.prism.impl.lex.json.reader; + +import java.io.IOException; +import java.util.Objects; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.impl.lex.json.JsonNullValueParser; +import com.evolveum.midpoint.prism.impl.lex.json.JsonValueParser; +import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.evolveum.midpoint.prism.xnode.ValueParser; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; + +/** + * TODO + */ +class JsonOtherTokenReader { + + private static final Trace LOGGER = TraceManager.getTrace(JsonOtherTokenReader.class); + + @NotNull private final JsonReadingContext ctx; + @NotNull private final JsonParser parser; + + JsonOtherTokenReader(JsonReadingContext ctx) { + this.ctx = ctx; + this.parser = ctx.parser; + } + + @NotNull XNodeImpl readValue() throws IOException, SchemaException { + JsonToken currentToken = Objects.requireNonNull(parser.currentToken(), "currentToken"); + + switch (currentToken) { + case START_OBJECT: + return new JsonObjectTokenReader(ctx).read(); + case START_ARRAY: + return parseToList(); + case VALUE_STRING: + case VALUE_TRUE: + case VALUE_FALSE: + case VALUE_NUMBER_FLOAT: + case VALUE_NUMBER_INT: + case VALUE_EMBEDDED_OBJECT: // assuming it's a scalar value e.g. !!binary (TODO) + return parseToPrimitive(); + case VALUE_NULL: + return parseToEmptyPrimitive(); + default: + throw new SchemaException("Unexpected current token: " + currentToken + ". At: " + ctx.getPositionSuffix()); + } + } + + private ListXNodeImpl parseToList() throws SchemaException, IOException { + Validate.notNull(parser.currentToken()); + + ListXNodeImpl list = new ListXNodeImpl(); + Object tid = parser.getTypeId(); + if (tid != null) { + list.setTypeQName(ctx.yamlTagResolver.tagToTypeName(tid, ctx)); + } + for (;;) { + JsonToken token = parser.nextToken(); + if (token == null) { + ctx.prismParsingContext.warnOrThrow(LOGGER, "Unexpected end of data while parsing a list structure at " + ctx.getPositionSuffix()); + return list; + } else if (token == JsonToken.END_ARRAY) { + return list; + } else { + list.add(readValue()); + } + } + } + + private PrimitiveXNodeImpl parseToPrimitive() throws IOException, SchemaException { + PrimitiveXNodeImpl primitive = new PrimitiveXNodeImpl<>(); + + Object tid = parser.getTypeId(); + if (tid != null) { + QName typeName = ctx.yamlTagResolver.tagToTypeName(tid, ctx); + primitive.setTypeQName(typeName); + primitive.setExplicitTypeDeclaration(true); + } else { + // We don't try to determine XNode type from the implicit JSON/YAML type (integer, number, ...), + // because XNode type prescribes interpretation in midPoint. E.g. YAML string type would be interpreted + // as xsd:string, even if the schema would expect e.g. timestamp. + } + + JsonNode jn = parser.readValueAs(JsonNode.class); + ValueParser vp = new JsonValueParser<>(parser, jn); + primitive.setValueParser(vp); + + return primitive; + } + + private PrimitiveXNodeImpl parseToEmptyPrimitive() { + PrimitiveXNodeImpl primitive = new PrimitiveXNodeImpl<>(); + primitive.setValueParser(new JsonNullValueParser<>()); + return primitive; + } + +// @SuppressWarnings("unused") // TODO remove if not needed +// private QName getCurrentTypeName(JsonReadingContext ctx) throws IOException, SchemaException { +// switch (parser.currentToken()) { +// case VALUE_NUMBER_INT: +// case VALUE_NUMBER_FLOAT: +// return AbstractReader.determineNumberType(parser.getNumberType()); +// case VALUE_FALSE: +// case VALUE_TRUE: +// return DOMUtil.XSD_BOOLEAN; +// case VALUE_STRING: +// return DOMUtil.XSD_STRING; +// case VALUE_NULL: +// return null; // TODO? +// default: +// throw new SchemaException("Unexpected current token type: " + parser.currentToken() + "/" + parser.getText() + " at " + ctx.getPositionSuffix()); +// } +// } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonReader.java new file mode 100644 index 00000000000..712f26a18be --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonReader.java @@ -0,0 +1,51 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.JsonFactory; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.schema.SchemaRegistry; + +public class JsonReader extends AbstractReader { + + public JsonReader(@NotNull SchemaRegistry schemaRegistry) { + super(schemaRegistry); + } + + @Override + public boolean canRead(@NotNull File file) { + return file.getName().endsWith(".json"); + } + + @Override + public boolean canRead(@NotNull String dataString) { + // Second for is for multiple objects + return dataString.startsWith("{") || dataString.startsWith("["); + } + + @Override + protected com.fasterxml.jackson.core.JsonParser createJacksonParser(InputStream stream) throws IOException { + return new JsonFactory().createParser(stream); + } + + @Override + protected QName tagToTypeName(Object tid, JsonReadingContext ctx) { + return null; + } + + @Override + boolean supportsMultipleDocuments() { + return false; + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonReadingContext.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonReadingContext.java new file mode 100644 index 00000000000..63723711ca9 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/JsonReadingContext.java @@ -0,0 +1,68 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.impl.ParsingContextImpl; +import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; +import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.fasterxml.jackson.core.JsonParser; +import org.jetbrains.annotations.NotNull; + +import java.util.IdentityHashMap; + +/** + * TODO + */ +class JsonReadingContext { + + @NotNull final JsonParser parser; + @NotNull final ParsingContextImpl prismParsingContext; + @NotNull final LexicalProcessor.RootXNodeHandler objectHandler; + @NotNull final AbstractReader.YamlTagResolver yamlTagResolver; + @NotNull final PrismContext prismContext; + + private boolean aborted; + + // TODO consider getting rid of these IdentityHashMaps by support default namespace marking and resolution + // directly in XNode structures (like it was done for Map XNode keys recently). + + // Definitions of namespaces ('@ns') within maps; to be applied after parsing. + @NotNull final IdentityHashMap defaultNamespaces = new IdentityHashMap<>(); + // Elements that should be skipped when filling-in default namespaces - those that are explicitly set with no-NS ('#name'). + // (Values for these entries are not important. Only key presence is relevant.) + @NotNull final IdentityHashMap noNamespaceElementNames = new IdentityHashMap<>(); + + JsonReadingContext(@NotNull JsonParser parser, @NotNull ParsingContextImpl prismParsingContext, + @NotNull LexicalProcessor.RootXNodeHandler objectHandler, @NotNull AbstractReader.YamlTagResolver yamlTagResolver, + @NotNull PrismContext prismContext) { + this.parser = parser; + this.prismParsingContext = prismParsingContext; + this.objectHandler = objectHandler; + this.yamlTagResolver = yamlTagResolver; + this.prismContext = prismContext; + } + + public boolean isAborted() { + return aborted; + } + + public void setAborted() { + this.aborted = true; + } + + String getPositionSuffix() { + return String.valueOf(parser.getCurrentLocation()); + } + + @NotNull + String getPositionSuffixIfPresent() { + return " At: " + getPositionSuffix(); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/MultiDocumentReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/MultiDocumentReader.java new file mode 100644 index 00000000000..83cf842d380 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/MultiDocumentReader.java @@ -0,0 +1,35 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import java.io.IOException; + +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * TODO better name + */ +class MultiDocumentReader { + + /** + * TODO + */ + @NotNull private final JsonReadingContext ctx; + + MultiDocumentReader(@NotNull JsonReadingContext ctx) { + this.ctx = ctx; + } + + public void read(boolean expectingMultipleObjects) throws IOException, SchemaException { + do { + new DocumentReader(ctx).read(expectingMultipleObjects); + } while (!ctx.isAborted() && ctx.parser.nextToken() != null); // YAML multi-document files + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/PolyStringDeserializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/PolyStringDeserializer.java similarity index 89% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/PolyStringDeserializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/PolyStringDeserializer.java index c8c0abdb43f..0e062eb0e81 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/PolyStringDeserializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/PolyStringDeserializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.reader; import java.io.IOException; @@ -24,6 +24,4 @@ public PolyString deserialize(JsonParser jp, DeserializationContext ctxt) throws String str = jp.getText(); return new PolyString(str); } - - } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/QNameDeserializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/QNameDeserializer.java similarity index 96% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/QNameDeserializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/QNameDeserializer.java index 38b787d660e..65b3aa5722a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/QNameDeserializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/QNameDeserializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.reader; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/RootObjectReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/RootObjectReader.java new file mode 100644 index 00000000000..5e6d2e03640 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/RootObjectReader.java @@ -0,0 +1,135 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import java.io.IOException; +import java.util.Map; +import javax.xml.namespace.QName; + +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.impl.lex.json.JsonValueParser; +import com.evolveum.midpoint.prism.impl.xnode.*; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.MetadataAware; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * TODO + */ +class RootObjectReader { + + static final String DEFAULT_NAMESPACE_MARKER = "##DEFAULT-NAMESPACE##"; + + @NotNull private final JsonReadingContext ctx; + + RootObjectReader(@NotNull JsonReadingContext ctx) { + this.ctx = ctx; + } + + void read() throws SchemaException, IOException { + XNodeImpl xnode = new JsonOtherTokenReader(ctx).readValue(); + RootXNodeImpl root = postProcessValueToRoot(xnode, null); + if (!ctx.objectHandler.handleData(root)) { + ctx.setAborted(); + } + } + + @NotNull + private RootXNodeImpl postProcessValueToRoot(XNodeImpl xnode, String defaultNamespace) throws SchemaException, IOException { + if (!xnode.isSingleEntryMap()) { + throw new SchemaException("Expected MapXNode with a single key; got " + xnode + " instead. At " + ctx.getPositionSuffix()); + } + processDefaultNamespaces(xnode, defaultNamespace, ctx); + processSchemaNodes(xnode); + Map.Entry entry = ((MapXNodeImpl) xnode).entrySet().iterator().next(); + RootXNodeImpl root = new RootXNodeImpl(entry.getKey(), entry.getValue()); + if (entry.getValue() != null) { + root.setTypeQName(entry.getValue().getTypeQName()); // TODO - ok ???? + } + return root; + } + + + // Default namespaces (@ns properties) are processed in the second pass, because they might be present within an object + // at any place, even at the end. + private void processDefaultNamespaces(XNodeImpl xnode, String parentDefault, JsonReadingContext ctx) { + if (xnode instanceof MapXNodeImpl) { + MapXNodeImpl map = (MapXNodeImpl) xnode; + String currentDefault = ctx.defaultNamespaces.getOrDefault(map, parentDefault); + map.replaceDefaultNamespaceMarkers(DEFAULT_NAMESPACE_MARKER, currentDefault); + for (Map.Entry entry : map.entrySet()) { + processDefaultNamespaces(entry.getValue(), currentDefault, ctx); + } + if (map.getMetadataNode() != null) { + processDefaultNamespaces((XNodeImpl) map.getMetadataNode(), currentDefault, ctx); + } + qualifyElementNameIfNeeded(map, currentDefault, ctx); + } else { + qualifyElementNameIfNeeded(xnode, parentDefault, ctx); + if (xnode instanceof ListXNodeImpl) { + for (XNodeImpl item : (ListXNodeImpl) xnode) { + processDefaultNamespaces(item, parentDefault, ctx); + } + } + if (xnode instanceof MetadataAware) { + MapXNode metadataNode = ((MetadataAware) xnode).getMetadataNode(); + if (metadataNode != null) { + processDefaultNamespaces((XNodeImpl) metadataNode, parentDefault, ctx); + } + } + } + } + + private void qualifyElementNameIfNeeded(XNodeImpl node, String namespace, JsonReadingContext ctx) { + if (node.getElementName() != null + && QNameUtil.noNamespace(node.getElementName()) + && StringUtils.isNotEmpty(namespace) + && !ctx.noNamespaceElementNames.containsKey(node)) { + node.setElementName(new QName(namespace, node.getElementName().getLocalPart())); + } + } + + // Schema nodes can be detected only after namespaces are resolved. + // We simply convert primitive nodes to schema ones. + private void processSchemaNodes(XNodeImpl xnode) throws SchemaException, IOException { + if (xnode instanceof MapXNodeImpl) { + MapXNodeImpl map = (MapXNodeImpl) xnode; + XNodeImpl schemaNode = null; + for (Map.Entry entry : map.entrySet()) { + QName fieldName = entry.getKey(); + XNodeImpl subnode = entry.getValue(); + if (DOMUtil.XSD_SCHEMA_ELEMENT.equals(fieldName)) { + schemaNode = subnode; + } else { + processSchemaNodes(subnode); + } + } + if (schemaNode != null) { + if (schemaNode instanceof PrimitiveXNodeImpl) { + PrimitiveXNodeImpl primitiveXNode = (PrimitiveXNodeImpl) schemaNode ; + if (primitiveXNode.isParsed()) { + throw new SchemaException("Cannot convert from PrimitiveXNode to SchemaXNode: node is already parsed: " + primitiveXNode); + } + SchemaXNodeImpl schemaXNode = new SchemaXNodeImpl(); + map.replace(DOMUtil.XSD_SCHEMA_ELEMENT, schemaXNode); + schemaXNode.setSchemaElement(((JsonValueParser) primitiveXNode.getValueParser()).asDomElement()); + } else { + throw new SchemaException("Cannot convert 'schema' field to SchemaXNode: not a PrimitiveNode but " + schemaNode); + } + } + } else if (xnode instanceof ListXNodeImpl) { + for (XNodeImpl item : (ListXNodeImpl) xnode) { + processSchemaNodes(item); + } + } + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/YamlReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/YamlReader.java new file mode 100644 index 00000000000..22865e92257 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/reader/YamlReader.java @@ -0,0 +1,111 @@ +/* + * 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.midpoint.prism.impl.lex.json.reader; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.JsonParser; +import org.jetbrains.annotations.NotNull; + +import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLFactory; +import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLParser; +import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; + +public class YamlReader extends AbstractReader { + + private static final String YAML = "tag:yaml.org,2002:"; + private static final String TAG_STRING = YAML + "str"; + private static final String TAG_INT = YAML + "int"; + private static final String TAG_BOOL = YAML + "bool"; + private static final String TAG_FLOAT = YAML + "float"; + private static final String TAG_BINARY = YAML + "binary"; // base64-encoded string + private static final String TAG_NULL = YAML + "null"; + + public YamlReader(@NotNull SchemaRegistry schemaRegistry) { + super(schemaRegistry); + } + + @Override + public boolean canRead(@NotNull File file) { + return file.getName().endsWith(".yaml"); + } + + @Override + public boolean canRead(@NotNull String dataString) { + return dataString.startsWith("---"); + } + + @Override + protected MidpointYAMLParser createJacksonParser(InputStream stream) throws IOException { + return (MidpointYAMLParser) new MidpointYAMLFactory().createParser(stream); + } + + @Override + protected QName tagToTypeName(Object tag, JsonReadingContext ctx) throws IOException, SchemaException { + if (tag == null) { + return null; + } if (TAG_BINARY.equals(tag)) { + return DOMUtil.XSD_BASE64BINARY; + } if (TAG_STRING.equals(tag)) { + return DOMUtil.XSD_STRING; + } else if (TAG_BOOL.equals(tag)) { + return DOMUtil.XSD_BOOLEAN; + } else if (TAG_NULL.equals(tag)) { + return null; // ??? + } else if (TAG_INT.equals(tag)) { + QName type = determineNumberType(ctx.parser.getNumberType()); + if (DOMUtil.XSD_INT.equals(type) || DOMUtil.XSD_INTEGER.equals(type)) { + return type; + } else { + return DOMUtil.XSD_INT; // suspicious + } + } else if (TAG_FLOAT.equals(tag)) { + QName type = determineNumberType(ctx.parser.getNumberType()); + if (DOMUtil.XSD_FLOAT.equals(type) || DOMUtil.XSD_DOUBLE.equals(type) || DOMUtil.XSD_DECIMAL.equals(type)) { + return type; + } else { + return DOMUtil.XSD_FLOAT; // suspicious + } + } else if (tag instanceof String) { + return QNameUtil.uriToQName((String) tag, true); + } else { + // TODO issue a warning? + return null; + } + } + + private static QName determineNumberType(JsonParser.NumberType numberType) throws SchemaException { + switch (numberType) { + case BIG_DECIMAL: + return DOMUtil.XSD_DECIMAL; + case BIG_INTEGER: + return DOMUtil.XSD_INTEGER; + case LONG: + return DOMUtil.XSD_LONG; + case INT: + return DOMUtil.XSD_INT; + case FLOAT: + return DOMUtil.XSD_FLOAT; + case DOUBLE: + return DOMUtil.XSD_DOUBLE; + default: + throw new SchemaException("Unsupported number type: " + numberType); + } + } + + @Override + boolean supportsMultipleDocuments() { + return true; + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/AbstractWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/AbstractWriter.java new file mode 100644 index 00000000000..c251b049be8 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/AbstractWriter.java @@ -0,0 +1,74 @@ +/* + * 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.midpoint.prism.impl.lex.json.writer; + +import java.io.IOException; +import java.util.List; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.evolveum.midpoint.prism.SerializationContext; +import com.evolveum.midpoint.prism.impl.lex.LexicalUtils; +import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.evolveum.midpoint.prism.xnode.RootXNode; +import com.evolveum.midpoint.prism.xnode.XNode; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; + +/** + * Writes XNode into JSON/YAML. + */ +abstract public class AbstractWriter { + + @NotNull + public String write(@NotNull XNode xnode, @NotNull QName rootElementName, SerializationContext prismSerializationContext) throws SchemaException{ + return write(LexicalUtils.createRootXNode((XNodeImpl) xnode, rootElementName), prismSerializationContext); + } + + @NotNull + public String write(@NotNull RootXNode root, SerializationContext prismSerializationContext) throws SchemaException { + return writeInternal((RootXNodeImpl) root, prismSerializationContext, false); + } + + @NotNull + public String write(@NotNull List roots, @Nullable SerializationContext prismSerializationContext) throws SchemaException { + ListXNodeImpl objectsList = new ListXNodeImpl(); + for (RootXNodeImpl root : roots) { + objectsList.add(root.toMapXNode()); + } + return writeInternal(objectsList, prismSerializationContext, true); + } + + @NotNull + private String writeInternal(@NotNull XNodeImpl root, SerializationContext prismSerializationContext, boolean useMultiDocument) throws SchemaException { + try (WritingContext ctx = createWritingContext(prismSerializationContext)) { + DocumentWriter documentWriter = new DocumentWriter(ctx); + if (root instanceof ListXNodeImpl && !root.isEmpty() && useMultiDocument && ctx.supportsMultipleDocuments()) { + // Note we cannot serialize empty lists in multi-document mode. + // It would result in empty content and an exception during serialization. + documentWriter.writeListAsSeparateDocuments((ListXNodeImpl) root); + } else { + documentWriter.write(root); + } + ctx.close(); // in order to get complete output + return ctx.getOutput(); + } catch (JsonProcessingException ex) { + throw new SchemaException("Error during writing to JSON/YAML: " + ex.getMessage(), ex); + } catch (IOException ioe) { + // There should be no IOExceptions as we are serializing to a string. + throw new SystemException("Error during writing to JSON/YAML: " + ioe.getMessage(), ioe); + } + } + + abstract WritingContext createWritingContext(SerializationContext prismSerializationContext); +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/DocumentWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/DocumentWriter.java new file mode 100644 index 00000000000..48d5d964368 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/DocumentWriter.java @@ -0,0 +1,264 @@ +/* + * 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.midpoint.prism.impl.lex.json.writer; + +import com.evolveum.midpoint.prism.SerializationOptions; +import com.evolveum.midpoint.prism.impl.lex.json.Constants; +import com.evolveum.midpoint.prism.impl.xnode.*; + +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.MetadataAware; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; + +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Writes single or multiple documents (JSON/YAML). + */ +class DocumentWriter { + + @NotNull private final WritingContext ctx; + + @NotNull private final JsonGenerator generator; + + private String currentNamespace; + + DocumentWriter(WritingContext ctx) { + this.ctx = ctx; + this.generator = ctx.generator; + } + + void writeListAsSeparateDocuments(@NotNull ListXNodeImpl root) throws IOException { + boolean first = true; + for (XNodeImpl item : root) { + if (!first) { + ctx.newDocument(); + } else { + first = false; + } + write(item); + } + } + + public void write(XNodeImpl xnode) throws IOException { + if (xnode instanceof RootXNodeImpl) { + write(((RootXNodeImpl) xnode).toMapXNode(), false); + } else { + write(xnode, false); + } + } + + private void write(XNodeImpl xnode, boolean wrappingValue) throws IOException { + if (xnode == null) { + writeNull(); + } else if (xnode instanceof MapXNodeImpl) { + writeMap((MapXNodeImpl) xnode); + } else if (!wrappingValue && needsValueWrapping(xnode)) { + writeWithValueWrapped(xnode); + } else if (xnode instanceof ListXNodeImpl) { + writeList((ListXNodeImpl) xnode); + } else if (xnode instanceof PrimitiveXNodeImpl) { + writePrimitive((PrimitiveXNodeImpl) xnode); + } else if (xnode instanceof SchemaXNodeImpl) { + writeSchema((SchemaXNodeImpl) xnode); + } else if (xnode instanceof IncompleteMarkerXNodeImpl) { + writeIncomplete(); + } else { + throw new UnsupportedOperationException("Cannot write " + xnode); + } + } + + private boolean needsValueWrapping(XNodeImpl xnode) { + return xnode.getElementName() != null + || getExplicitType(xnode) != null && !ctx.supportsInlineTypes() + || xnode.hasMetadata(); + } + + private void writeNull() throws IOException { + generator.writeNull(); + } + + private void writeWithValueWrapped(XNodeImpl xnode) throws IOException { + assert !(xnode instanceof MapXNode); + generator.writeStartObject(); + ctx.resetInlineTypeIfPossible(); + writeElementAndTypeIfNeeded(xnode); + generator.writeFieldName(Constants.PROP_VALUE); + write(xnode, true); + writeMetadataIfNeeded(xnode); + generator.writeEndObject(); + } + + private void writeMap(MapXNodeImpl map) throws IOException { + writeInlineTypeIfNeeded(map); + generator.writeStartObject(); + ctx.resetInlineTypeIfPossible(); + String oldDefaultNamespace = currentNamespace; + writeNsDeclarationIfNeeded(map); + writeElementAndTypeIfNeeded(map); + writeMetadataIfNeeded(map); + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() != null) { + generator.writeFieldName(createKeyUri(entry)); + write(entry.getValue(), false); + } + } + generator.writeEndObject(); + currentNamespace = oldDefaultNamespace; + } + + private void writeList(ListXNodeImpl list) throws IOException { + writeInlineTypeIfNeeded(list); + generator.writeStartArray(); + ctx.resetInlineTypeIfPossible(); + for (XNodeImpl item : list) { + write(item, false); + } + generator.writeEndArray(); + } + + private void writePrimitive(PrimitiveXNodeImpl primitive) throws IOException { + writeInlineTypeIfNeeded(primitive); + if (primitive.isParsed()) { + generator.writeObject(primitive.getValue()); + } else { + generator.writeObject(primitive.getStringValue()); + } + } + + private void writeSchema(SchemaXNodeImpl node) throws IOException { + writeInlineTypeIfNeeded(node); + Element schemaElement = node.getSchemaElement(); + DOMUtil.fixNamespaceDeclarations(schemaElement); // TODO reconsider if it's OK to modify schema DOM element in this way + generator.writeObject(schemaElement); + } + + private void writeIncomplete() throws IOException { + generator.writeStartObject(); + generator.writeFieldName(Constants.PROP_INCOMPLETE); + generator.writeBoolean(true); + generator.writeEndObject(); + } + + private void writeElementAndTypeIfNeeded(XNodeImpl xnode) throws IOException { + QName elementName = xnode.getElementName(); + if (elementName != null) { + generator.writeObjectField(Constants.PROP_ELEMENT, createElementNameUri(elementName)); + } + QName typeName = getExplicitType(xnode); + if (typeName != null) { + if (!ctx.supportsInlineTypes()) { + generator.writeObjectField(Constants.PROP_TYPE, typeName); + } + } + } + + private void writeMetadataIfNeeded(XNodeImpl xnode) throws IOException { + if (xnode instanceof MetadataAware) { + MapXNode metadataNode = ((MetadataAware) xnode).getMetadataNode(); + if (metadataNode != null) { + generator.writeFieldName(Constants.PROP_METADATA); + writeMap((MapXNodeImpl) metadataNode); + } + } + } + + private void writeNsDeclarationIfNeeded(MapXNodeImpl map) throws IOException { + SerializationOptions opts = ctx.prismSerializationContext.getOptions(); + if (!SerializationOptions.isUseNsProperty(opts) || map.isEmpty()) { + return; + } + String namespace = determineNewCurrentNamespace(map); + if (namespace != null && !StringUtils.equals(namespace, currentNamespace)) { + currentNamespace = namespace; + generator.writeFieldName(Constants.PROP_NAMESPACE); + generator.writeString(namespace); + } + } + + private String determineNewCurrentNamespace(MapXNodeImpl map) { + Map counts = new HashMap<>(); + for (QName childName : map.keySet()) { + String childNs = childName.getNamespaceURI(); + if (StringUtils.isEmpty(childNs)) { + continue; + } + if (childNs.equals(currentNamespace)) { + return currentNamespace; // found existing => continue with it + } + increaseCounter(counts, childNs); + } + if (map.getElementName() != null && QNameUtil.hasNamespace(map.getElementName())) { + increaseCounter(counts, map.getElementName().getNamespaceURI()); + } + // otherwise, take the URI that occurs the most in the map + Map.Entry max = null; + for (Map.Entry count : counts.entrySet()) { + if (max == null || count.getValue() > max.getValue()) { + max = count; + } + } + return max != null ? max.getKey() : null; + } + + private void increaseCounter(Map counts, String childNs) { + Integer c = counts.get(childNs); + counts.put(childNs, c != null ? c+1 : 1); + } + + private String createKeyUri(Map.Entry entry) { + QName key = entry.getKey(); + if (namespaceMatch(currentNamespace, key.getNamespaceURI())) { + return key.getLocalPart(); + } else if (StringUtils.isNotEmpty(currentNamespace) && !isAttribute(entry.getValue())) { + return QNameUtil.qNameToUri(key, true); // items with no namespace should be written as such (starting with '#') + } else { + return QNameUtil.qNameToUri(key, false); // items with no namespace can be written in plain + } + } + + private String createElementNameUri(QName elementName) { + if (namespaceMatch(currentNamespace, elementName.getNamespaceURI())) { + return elementName.getLocalPart(); + } else { + return QNameUtil.qNameToUri(elementName, StringUtils.isNotEmpty(currentNamespace)); + } + } + + private boolean isAttribute(XNodeImpl node) { + return node instanceof PrimitiveXNodeImpl && ((PrimitiveXNodeImpl) node).isAttribute(); + } + + private boolean namespaceMatch(String currentNamespace, String itemNamespace) { + if (StringUtils.isEmpty(currentNamespace)) { + return StringUtils.isEmpty(itemNamespace); + } else { + return currentNamespace.equals(itemNamespace); + } + } + + private void writeInlineTypeIfNeeded(XNodeImpl node) throws IOException { + QName explicitType = getExplicitType(node); + if (ctx.supportsInlineTypes() && explicitType != null) { + ctx.writeInlineType(explicitType); + } + } + + private QName getExplicitType(XNodeImpl xnode) { + return xnode.isExplicitTypeDeclaration() ? xnode.getTypeQName() : null; + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/DomElementSerializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/DomElementSerializer.java similarity index 84% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/DomElementSerializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/DomElementSerializer.java index 71272240943..95796b79889 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/DomElementSerializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/DomElementSerializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; import java.io.IOException; @@ -21,8 +21,6 @@ public class DomElementSerializer extends DOMSerializer { @Override public void serializeWithType(Node value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException { - // TODO Auto-generated method stub serialize(value, jgen, provider); } - } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathSerializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/ItemPathSerializer.java similarity index 90% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathSerializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/ItemPathSerializer.java index f2d4c8b1777..fe79bcef435 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathSerializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/ItemPathSerializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathTypeSerializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/ItemPathTypeSerializer.java similarity index 90% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathTypeSerializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/ItemPathTypeSerializer.java index 1fbbc8bb6dc..e073883bda6 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/ItemPathTypeSerializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/ItemPathTypeSerializer.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; import com.evolveum.midpoint.prism.impl.marshaller.ItemPathSerializerTemp; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/JsonWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/JsonWriter.java new file mode 100644 index 00000000000..7fbd3a093da --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/JsonWriter.java @@ -0,0 +1,18 @@ +/* + * 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.prism.impl.lex.json.writer; + +import com.evolveum.midpoint.prism.SerializationContext; + +public class JsonWriter extends AbstractWriter { + + @Override + WritingContext createWritingContext(SerializationContext prismSerializationContext) { + return new JsonWritingContext(prismSerializationContext); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/JsonWritingContext.java similarity index 55% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonLexicalProcessor.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/JsonWritingContext.java index e6094e379f9..4c9f2f933e1 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/JsonLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/JsonWritingContext.java @@ -1,18 +1,19 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * 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.midpoint.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; +import com.evolveum.midpoint.prism.SerializationContext; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.Version; @@ -22,68 +23,43 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; -import org.jetbrains.annotations.NotNull; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; -import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.StringWriter; -public class JsonLexicalProcessor extends AbstractJsonLexicalProcessor { - - public JsonLexicalProcessor(@NotNull SchemaRegistry schemaRegistry) { - super(schemaRegistry); - } - - @Override - public boolean canRead(@NotNull File file) throws IOException { - return file.getName().endsWith(".json"); - } - - @Override - public boolean canRead(@NotNull String dataString) { - return dataString.startsWith("{"); - } +/** + * TODO + */ +class JsonWritingContext extends WritingContext { - @Override - protected com.fasterxml.jackson.core.JsonParser createJacksonParser(InputStream stream) throws SchemaException, IOException { - JsonFactory factory = new JsonFactory(); - try { - return factory.createParser(stream); - } catch (IOException e) { - throw e; - } + JsonWritingContext(SerializationContext prismSerializationContext) { + super(prismSerializationContext); } - public JsonGenerator createJacksonGenerator(StringWriter out) throws SchemaException{ - return createJsonGenerator(out); - } - private JsonGenerator createJsonGenerator(StringWriter out) throws SchemaException{ + JsonGenerator createJacksonGenerator(StringWriter out) { try { JsonFactory factory = new JsonFactory(); JsonGenerator generator = factory.createGenerator(out); generator.setPrettyPrinter(new DefaultPrettyPrinter()); generator.setCodec(configureMapperForSerialization()); - return generator; - } catch (IOException ex){ - throw new SchemaException("Schema error during serializing to JSON.", ex); + } catch (IOException ex) { + throw new SystemException("Couldn't create Jackson generator for JSON: " + ex.getMessage(), ex); } - } - private ObjectMapper configureMapperForSerialization(){ + private ObjectMapper configureMapperForSerialization() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); - mapper.setSerializationInclusion(Include.NON_NULL); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.registerModule(createSerializerModule()); mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector()); return mapper; } - private Module createSerializerModule(){ + private Module createSerializerModule() { SimpleModule module = new SimpleModule("MidpointModule", new Version(0, 0, 0, "aa")); module.addSerializer(QName.class, new QNameSerializer()); module.addSerializer(PolyString.class, new PolyStringSerializer()); @@ -95,18 +71,26 @@ private Module createSerializerModule(){ return module; } + protected boolean supportsInlineTypes() { + return false; + } + @Override - protected QName tagToTypeName(Object tid, AbstractJsonLexicalProcessor.JsonParsingContext ctx) { - return null; + void writeInlineType(QName typeName) { + throw new UnsupportedOperationException(); } @Override - protected boolean supportsInlineTypes() { + void resetInlineTypeIfPossible() { + } + + @Override + boolean supportsMultipleDocuments() { return false; } @Override - protected void writeInlineType(QName typeName, JsonSerializationContext ctx) throws IOException { - throw new IllegalStateException("JSON cannot write type information using tags."); + void newDocument() { + throw new UnsupportedOperationException(); } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/PolyStringSerializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/PolyStringSerializer.java similarity index 90% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/PolyStringSerializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/PolyStringSerializer.java index 61e9ef7bf40..4d466933d8b 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/PolyStringSerializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/PolyStringSerializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/QNameSerializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/QNameSerializer.java similarity index 89% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/QNameSerializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/QNameSerializer.java index 3440abb5f3f..e7f23eccc2c 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/QNameSerializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/QNameSerializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/WritingContext.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/WritingContext.java new file mode 100644 index 00000000000..9d081bb4bcc --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/WritingContext.java @@ -0,0 +1,62 @@ +/* + * 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.midpoint.prism.impl.lex.json.writer; + +import com.evolveum.midpoint.prism.SerializationContext; + +import com.evolveum.midpoint.util.exception.SystemException; + +import com.fasterxml.jackson.core.JsonGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.xml.namespace.QName; +import java.io.IOException; +import java.io.StringWriter; + +/** + * TODO + */ +abstract class WritingContext implements AutoCloseable { + + @NotNull private final StringWriter out = new StringWriter(); + @NotNull final G generator; + @NotNull final SerializationContext prismSerializationContext; + + WritingContext(@Nullable SerializationContext prismSerializationContext) { + this.generator = createJacksonGenerator(out); + this.prismSerializationContext = prismSerializationContext != null ? + prismSerializationContext : + new SerializationContext(null); + } + + abstract G createJacksonGenerator(StringWriter out); + + String getOutput() { + return out.toString(); + } + + @Override + public void close() { + try { + generator.close(); + } catch (IOException e) { + throw new SystemException("Error writing to JSON/YAML: " + e.getMessage(), e); + } + } + + abstract boolean supportsInlineTypes(); + + abstract void writeInlineType(QName typeName) throws IOException; + + abstract void resetInlineTypeIfPossible(); + + abstract boolean supportsMultipleDocuments(); + + abstract void newDocument() throws IOException; +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/XmlGregorianCalendarSerializer.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/XmlGregorianCalendarSerializer.java similarity index 89% rename from infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/XmlGregorianCalendarSerializer.java rename to infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/XmlGregorianCalendarSerializer.java index 5e67fcba91f..9b874028871 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/XmlGregorianCalendarSerializer.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/XmlGregorianCalendarSerializer.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * 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.prism.impl.lex.json; +package com.evolveum.midpoint.prism.impl.lex.json.writer; import java.io.IOException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/YamlWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/YamlWriter.java new file mode 100644 index 00000000000..eaf452d7d87 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/YamlWriter.java @@ -0,0 +1,18 @@ +/* + * 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.prism.impl.lex.json.writer; + +import com.evolveum.midpoint.prism.SerializationContext; + +public class YamlWriter extends AbstractWriter { + + @Override + YamlWritingContext createWritingContext(SerializationContext prismSerializationContext) { + return new YamlWritingContext(prismSerializationContext); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/YamlWritingContext.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/YamlWritingContext.java new file mode 100644 index 00000000000..de22454026c --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/writer/YamlWritingContext.java @@ -0,0 +1,105 @@ +/* + * 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.midpoint.prism.impl.lex.json.writer; + +import java.io.IOException; +import java.io.StringWriter; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Element; + +import com.evolveum.midpoint.prism.SerializationContext; +import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLFactory; +import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLGenerator; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; + +public class YamlWritingContext extends WritingContext { + + YamlWritingContext(@Nullable SerializationContext prismSerializationContext) { + super(prismSerializationContext); + } + + MidpointYAMLGenerator createJacksonGenerator(StringWriter out) { + try { + MidpointYAMLFactory factory = new MidpointYAMLFactory(); + MidpointYAMLGenerator generator = (MidpointYAMLGenerator) factory.createGenerator(out); + generator.setPrettyPrinter(new DefaultPrettyPrinter()); + generator.setCodec(configureMapperForSerialization()); + return generator; + } catch (IOException ex) { + throw new SystemException("Couldn't create Jackson generator for YAML: " + ex.getMessage(), ex); + } + } + + private ObjectMapper configureMapperForSerialization() { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); +// mapper.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS, As.EXISTING_PROPERTY); +// mapper.configure(SerializationFeaCture.); +// mapper.setSerializationInclusion(Include.NON_NULL); + mapper.registerModule(createSerializerModule()); + + mapper.disableDefaultTyping(); + +// mapper.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS); +// mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.EXISTING_PROPERTY); +// //mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.EXTERNAL_PROPERTY); +// mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); + + return mapper; + } + + private Module createSerializerModule(){ + SimpleModule module = new SimpleModule("MidpointModule", new Version(0, 0, 0, "aa")); + module.addSerializer(QName.class, new QNameSerializer()); + module.addSerializer(PolyString.class, new PolyStringSerializer()); + module.addSerializer(ItemPath.class, new ItemPathSerializer()); + module.addSerializer(ItemPathType.class, new ItemPathTypeSerializer()); +// module.addSerializer(JAXBElement.class, new JaxbElementSerializer()); + module.addSerializer(XMLGregorianCalendar.class, new XmlGregorianCalendarSerializer()); + module.addSerializer(Element.class, new DomElementSerializer()); + return module; + } + + @Override + protected boolean supportsInlineTypes() { + return true; + } + + @Override + protected void writeInlineType(QName typeName) throws IOException { + generator.writeTypeId(QNameUtil.qNameToUri(typeName, false, '/')); + } + + @Override + protected void resetInlineTypeIfPossible() { + generator.resetTypeId(); // brutal hack + } + + @Override + boolean supportsMultipleDocuments() { + return true; + } + + @Override + void newDocument() throws IOException { + generator.newDocument(); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/JaxbDomHackImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/JaxbDomHackImpl.java index 6eeb3433b9d..3dffc34d907 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/JaxbDomHackImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/JaxbDomHackImpl.java @@ -46,11 +46,11 @@ public class JaxbDomHackImpl implements JaxbDomHack { private static final Trace LOGGER = TraceManager.getTrace(JaxbDomHack.class); private PrismContext prismContext; - private DomLexicalProcessor domParser; + private DomLexicalProcessor domLexicalProcessor; - public JaxbDomHackImpl(DomLexicalProcessor domParser, PrismContext prismContext) { + public JaxbDomHackImpl(DomLexicalProcessor domLexicalProcessor, PrismContext prismContext) { super(); - this.domParser = domParser; + this.domLexicalProcessor = domLexicalProcessor; this.prismContext = prismContext; } @@ -216,7 +216,7 @@ public Object toAny(PrismValue value) throws SchemaException { if (pval.isRaw() && parent.getDefinition() == null) { XNodeImpl rawElement = (XNodeImpl) pval.getRawElement(); if (rawElement instanceof MapXNodeImpl) { - return domParser.serializeXMapToElement((MapXNodeImpl)rawElement, elementName); + return domLexicalProcessor.writeXMapToElement((MapXNodeImpl)rawElement, elementName); } else if (rawElement instanceof PrimitiveXNodeImpl) { PrimitiveXNodeImpl xprim = (PrimitiveXNodeImpl)rawElement; String stringValue = xprim.getStringValue(); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java index d0f11414735..0eaa5f7f511 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java @@ -13,6 +13,8 @@ import com.evolveum.midpoint.prism.schema.SchemaRegistry; import com.evolveum.midpoint.prism.impl.util.PrismUtilInternal; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.MetadataAware; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.JAXBUtil; import com.evolveum.midpoint.util.QNameUtil; @@ -35,6 +37,8 @@ import java.util.Collection; import java.util.List; +import static java.util.Collections.emptySet; + /** * @author semancik * @@ -145,14 +149,6 @@ RootXNodeImpl marshalAnyData(@NotNull Object object, QName itemName, ItemDefinit } } - public boolean canSerialize(Object object) { - if (object instanceof Item) { - return true; - } else { - return beanMarshaller.canProcess(object.getClass()); - } - } - /* * TODO reconsider what to return for empty items * 1. null @@ -206,7 +202,7 @@ private XNodeImpl marshalItemValue(@NotNull PrismValue itemValue, @Nullable Item XNodeImpl xnode; if (definition == null && typeName == null && itemValue instanceof PrismPropertyValue) { warnIfItemsToSkip(itemValue, itemsToSkip); - return serializePropertyRawValue((PrismPropertyValue) itemValue); + xnode = serializePropertyRawValue((PrismPropertyValue) itemValue); } else if (itemValue instanceof PrismReferenceValue) { warnIfItemsToSkip(itemValue, itemsToSkip); xnode = serializeReferenceValue((PrismReferenceValue)itemValue, (PrismReferenceDefinition) definition, ctx); @@ -224,9 +220,23 @@ private XNodeImpl marshalItemValue(@NotNull PrismValue itemValue, @Nullable Item } xnode.setExplicitTypeDeclaration(true); } + marshalValueMetadata(itemValue, xnode, ctx); return xnode; } + private void marshalValueMetadata(PrismValue itemValue, XNodeImpl xnode, SerializationContext ctx) throws SchemaException { + PrismContainerValue valueMetadata = itemValue.getValueMetadata(); + if (!valueMetadata.isEmpty()) { + if (xnode instanceof MetadataAware) { + MapXNode metadataNode = marshalContainerValue(valueMetadata, + getSchemaRegistry().getValueMetadataDefinition(), ctx, emptySet()); + ((MetadataAware) xnode).setMetadataNode(metadataNode); + } else { + throw new IllegalStateException("Couldn't marshal value metadata of " + itemValue + " to non-metadata-aware XNode: " + xnode); + } + } + } + private void warnIfItemsToSkip(@NotNull PrismValue itemValue, Collection itemsToSkip) { if (CollectionUtils.isNotEmpty(itemsToSkip)) { LOGGER.warn("Trying to skip marshalling items {} where not applicable: {}", itemsToSkip, itemValue); @@ -270,16 +280,17 @@ private boolean isInstantiable(ItemDefinition definition) { } } - private MapXNodeImpl marshalContainerValue(PrismContainerValue containerVal, - PrismContainerDefinition containerDefinition, SerializationContext ctx, + @NotNull + private MapXNodeImpl marshalContainerValue(PrismContainerValue containerVal, + PrismContainerDefinition containerDefinition, SerializationContext ctx, Collection itemsToSkip) throws SchemaException { MapXNodeImpl xmap = new MapXNodeImpl(); marshalContainerValue(xmap, containerVal, containerDefinition, ctx, itemsToSkip); return xmap; } - private void marshalContainerValue(MapXNodeImpl xmap, PrismContainerValue containerVal, - PrismContainerDefinition containerDefinition, SerializationContext ctx, + private void marshalContainerValue(MapXNodeImpl xmap, PrismContainerValue containerVal, + PrismContainerDefinition containerDefinition, SerializationContext ctx, Collection itemsToSkip) throws SchemaException { Long id = containerVal.getId(); if (id != null) { diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismSerializerImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismSerializerImpl.java index 6d20d1c248c..da72c928782 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismSerializerImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismSerializerImpl.java @@ -16,7 +16,6 @@ import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; import com.evolveum.midpoint.util.exception.SchemaException; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; @@ -138,7 +137,7 @@ public T serialize(@NotNull RootXNode xnode) throws SchemaException { @NotNull @Override - public T serializeObjects(@NotNull List> objects, @Nullable QName aggregateElementName) throws SchemaException { + public T serializeObjects(@NotNull List> objects) throws SchemaException { List roots = new ArrayList<>(); for (PrismObject object : objects) { // itemName and itemDefinition might be set only if they apply to all the objects @@ -146,7 +145,7 @@ public T serializeObjects(@NotNull List> objects, @Nullable QName checkPostconditions(xroot); // TODO find better way roots.add(xroot); } - return target.write(roots, aggregateElementName, context); + return target.write(roots, context); } @Override diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java index 2085e053082..42531f78302 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java @@ -17,34 +17,16 @@ import javax.xml.XMLConstants; import javax.xml.namespace.QName; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.impl.metadata.ValueMetadataAdapter; +import com.evolveum.midpoint.prism.xnode.*; + import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.Validate; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.evolveum.midpoint.prism.ComplexTypeDefinition; -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.prism.ExpressionWrapper; -import com.evolveum.midpoint.prism.Item; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.Objectable; -import com.evolveum.midpoint.prism.ParsingContext; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerDefinition; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.PrismPropertyDefinition; -import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.PrismReference; -import com.evolveum.midpoint.prism.PrismReferenceDefinition; -import com.evolveum.midpoint.prism.PrismReferenceValue; -import com.evolveum.midpoint.prism.PrismValue; -import com.evolveum.midpoint.prism.SchemaMigration; -import com.evolveum.midpoint.prism.SchemaMigrationOperation; -import com.evolveum.midpoint.prism.TypeDefinition; import com.evolveum.midpoint.prism.impl.PrismContainerDefinitionImpl; import com.evolveum.midpoint.prism.impl.PrismContainerValueImpl; import com.evolveum.midpoint.prism.impl.PrismPropertyDefinitionImpl; @@ -63,9 +45,6 @@ import com.evolveum.midpoint.prism.schema.PrismSchema; import com.evolveum.midpoint.prism.schema.SchemaRegistry; import com.evolveum.midpoint.prism.util.PrismUtil; -import com.evolveum.midpoint.prism.xnode.PrimitiveXNode; -import com.evolveum.midpoint.prism.xnode.RootXNode; -import com.evolveum.midpoint.prism.xnode.XNode; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.QNameUtil; @@ -97,7 +76,7 @@ public class PrismUnmarshaller { XNodeImpl.KEY_REFERENCE_OBJECT.getLocalPart(), XNodeImpl.KEY_REFERENCE_REFERENTIAL_INTEGRITY.getLocalPart()); - @NotNull private PrismContext prismContext; + @NotNull private final PrismContext prismContext; @NotNull private final BeanUnmarshaller beanUnmarshaller; @NotNull private final SchemaRegistryImpl schemaRegistry; @@ -148,7 +127,6 @@ private PrismObject parseObject(MapXNodeImpl map, Pris return (PrismObject) parseItemInternal(map, itemInfo.getItemName(), itemInfo.getItemDefinition(), pc); } - Item parseItem(@NotNull RootXNodeImpl root, ItemDefinition itemDefinition, QName itemName, QName typeName, Class typeClass, @NotNull ParsingContext pc) throws SchemaException { @@ -278,22 +256,26 @@ private Long getContainerId(MapXNodeImpl xmap) throws SchemaException { private PrismContainerValue parseContainerValue(@NotNull XNodeImpl node, @NotNull PrismContainerDefinition containerDef, @NotNull ParsingContext pc) throws SchemaException { + PrismContainerValue rv; if (node instanceof MapXNodeImpl) { - return parseContainerValueFromMap((MapXNodeImpl) node, containerDef, pc); + rv = parseContainerValueFromMap((MapXNodeImpl) node, containerDef, pc); } else if (node instanceof PrimitiveXNodeImpl) { PrimitiveXNodeImpl prim = (PrimitiveXNodeImpl) node; if (prim.isEmpty()) { - return containerDef.createValue(); + rv = containerDef.createValue(); } else { pc.warnOrThrow(LOGGER, "Cannot parse container value from (non-empty) " + node); - return containerDef.createValue(); + rv = containerDef.createValue(); } } else { pc.warnOrThrow(LOGGER, "Cannot parse container value from " + node); - return containerDef.createValue(); + rv = containerDef.createValue(); } + addMetadataIfPresent(rv, node, pc); + return rv; } + @NotNull private PrismContainerValue parseContainerValueFromMap(@NotNull MapXNodeImpl map, @NotNull PrismContainerDefinition containerDef, @NotNull ParsingContext pc) throws SchemaException { Long id = getContainerId(map); @@ -302,6 +284,7 @@ private PrismContainerValue parseContainerValueFrom PrismContainerValue cval; if (containerDef instanceof PrismObjectDefinition) { + //noinspection unchecked cval = ((PrismObjectDefinition) containerDef).createValue(); } else { // override container definition, if explicit type is specified @@ -358,7 +341,7 @@ private void parseContainerChildren(PrismContainerValue cval, MapXNodeImpl ma } private boolean handleMissingDefinition(QName itemName, ItemDefinition containerDef, TypeDefinition typeDefinition, ParsingContext pc, DebugDumpable object) throws SchemaException { - SchemaMigration migration = determineSchemaMigration(typeDefinition, itemName, pc); + SchemaMigration migration = determineSchemaMigration(typeDefinition, itemName); if (migration != null && pc.isCompat()) { if (migration.getOperation() == SchemaMigrationOperation.REMOVED) { String msg = "Item "+itemName+" was removed from the schema, skipped processing of that item"; @@ -391,7 +374,7 @@ private boolean handleMissingDefinition(QName itemName, ItemDefinition contai return false; } - private SchemaMigration determineSchemaMigration(TypeDefinition typeDefinition, QName itemName, @NotNull ParsingContext pc) { + private SchemaMigration determineSchemaMigration(TypeDefinition typeDefinition, QName itemName) { if (typeDefinition == null) { return null; } @@ -411,7 +394,7 @@ private SchemaMigration determineSchemaMigration(TypeDefinition typeDefinition, if (superTypeDef == null) { return null; } - return determineSchemaMigration(superTypeDef, itemName, pc); + return determineSchemaMigration(superTypeDef, itemName); } @NotNull @@ -454,7 +437,7 @@ private PrismProperty parseProperty(@NotNull XNodeImpl node, @NotNull QNa } else if (node instanceof SchemaXNodeImpl) { SchemaDefinitionType schemaDefType = beanUnmarshaller.unmarshalSchemaDefinitionType((SchemaXNodeImpl) node); @SuppressWarnings("unchecked") - PrismPropertyValue val = new PrismPropertyValueImpl(schemaDefType); + PrismPropertyValue val = (PrismPropertyValue) new PrismPropertyValueImpl<>(schemaDefType); addItemValueIfPossible(property, val, pc); } else if (node instanceof IncompleteMarkerXNodeImpl) { property.setIncomplete(true); @@ -479,48 +462,79 @@ private PrismPropertyValue parsePropertyValue(@NotNull XNodeImpl node, @Nullable PrismPropertyDefinition definition, @NotNull ParsingContext pc) throws SchemaException { QName typeFromDefinition = definition != null && !definition.isAnyType() ? definition.getTypeName() : null; QName typeName = ((SchemaRegistry) schemaRegistry).selectMoreSpecific(typeFromDefinition, node.getTypeQName()); + + PrismPropertyValue rv; if (typeName == null) { return createRawPrismPropertyValue(node); } else if (beanUnmarshaller.canProcess(typeName)) { - T realValue = beanUnmarshaller.unmarshal(node, typeName, pc); - // Postprocessing after returning from unmarshaller. It speaks bean language (e.g. PolyStringType, not PolyString). - // It also doesn't know about prism-specific things like allowed values, etc. - if (realValue instanceof PolyStringType) { - @SuppressWarnings("unchecked") - T valueT = (T) ((PolyStringType) realValue).toPolyString(); - realValue = valueT; - } - PrismUtil.recomputeRealValue(realValue, prismContext); + Object unmarshalled = beanUnmarshaller.unmarshal(node, typeName, pc); + T realValue = treatPolyStringAndRecompute(unmarshalled); if (!isValueAllowed(realValue, definition)) { pc.warnOrThrow(LOGGER, "Unknown (not allowed) value of type " + typeName + ". Value: " + realValue + ". Allowed values: " + definition.getAllowedValues()); - return null; - } - if (realValue == null) { - // Be careful here. Expression element can be legal sub-element of complex properties. - // Therefore parse expression only if there is no legal value. - ExpressionWrapper expression = PrismUtilInternal.parseExpression(node, prismContext); - if (expression != null) { - return new PrismPropertyValueImpl<>(null, prismContext, null, null, expression); - } else { - // There's no point in returning PPV(null) as it would soon fail on internal PP check. - // We are probably recovering from an error in COMPAT mode here, so let's just skip this value. - return null; - } + rv = null; + } else if (realValue == null) { + rv = deriveValueFromExpression(node); + } else { + PrismPropertyValueImpl ppv = new PrismPropertyValueImpl<>(realValue); + ppv.setPrismContext(prismContext); + rv = ppv; } - PrismPropertyValueImpl ppv = new PrismPropertyValueImpl<>(realValue); - ppv.setPrismContext(prismContext); - return ppv; } else { pc.warnOrThrow(LOGGER, "Cannot parse as " + typeName + " because bean unmarshaller cannot process it (generated bean classes are missing?): " + node.debugDump()); - return createRawPrismPropertyValue(node); + rv = createRawPrismPropertyValue(node); + } + addMetadataIfPresent(rv, node, pc); + return rv; + } + + private void addMetadataIfPresent(PrismValue prismValue, XNode node, @NotNull ParsingContext pc) throws SchemaException { + if (prismValue != null && node instanceof MetadataAware) { + MapXNode metadata = ((MetadataAware) node).getMetadataNode(); + if (metadata != null) { + prismValue.setValueMetadata(parseMetadata(metadata, pc)); + } + } + } + + private ValueMetadata parseMetadata(MapXNode metadata, ParsingContext pc) throws SchemaException { + PrismContainerValue pcv = + parseContainerValueFromMap((MapXNodeImpl) metadata, schemaRegistry.getValueMetadataDefinition(), pc); + return ValueMetadataAdapter.holding(pcv); + } + + @Nullable + private PrismPropertyValue deriveValueFromExpression(@NotNull XNodeImpl node) throws SchemaException { + // Be careful here. Expression element can be legal sub-element of complex properties. + // Therefore parse expression only if there is no legal value. + ExpressionWrapper expression = PrismUtilInternal.parseExpression(node, prismContext); + if (expression != null) { + return new PrismPropertyValueImpl<>(null, prismContext, null, null, expression); + } else { + // There's no point in returning PPV(null) as it would soon fail on internal PP check. + // We are probably recovering from an error in COMPAT mode here, so let's just skip this value. + return null; } } + // Postprocessing after returning from unmarshaller. It speaks bean language (e.g. PolyStringType, not PolyString). + private T treatPolyStringAndRecompute(Object bean) { + Object rv; + if (bean instanceof PolyStringType) { + rv = ((PolyStringType) bean).toPolyString(); + } else { + rv = bean; + } + PrismUtil.recomputeRealValue(rv, prismContext); + //noinspection unchecked + return (T) rv; + } + @NotNull private PrismPropertyValue createRawPrismPropertyValue(@NotNull XNodeImpl node) { return prismContext.itemFactory().createPropertyValue(node); } + @Contract("_, null -> true") private boolean isValueAllowed(T realValue, PrismPropertyDefinition definition) { if (realValue instanceof Enum) { // Statically-defined enums have been already treated. Unless someone overrides the static schema, @@ -529,7 +543,7 @@ private boolean isValueAllowed(T realValue, PrismPropertyDefinition defin } else if (definition == null || CollectionUtils.isEmpty(definition.getAllowedValues())) { return true; } else if (realValue == null) { - return true; // TODO: ok? + return true; // TODO: ok? } else { return definition.getAllowedValues().stream() .anyMatch(displayableValue -> realValue.equals(displayableValue.getValue())); @@ -577,15 +591,18 @@ private PrismReferenceValue parseReferenceValueFromXNode(@NotNull XNodeImpl node isComposite = !QNameUtil.match(itemName, definition.getItemName()); } - + PrismReferenceValue rv; if (isComposite) { - return parseReferenceValueAsCompositeObject(node, definition, pc); // This is a composite object (complete object stored inside reference) + rv = parseReferenceValueAsCompositeObject(node, definition, pc); // This is a composite object (complete object stored inside reference) } else { // TODO fix this hack: for delta values of ObjectReferenceType we will not - // insist on having reference type (because the target definition could be such that it won't require it) + // insist on having reference type (because the target definition could be such that it won't require it) boolean allowMissingRefTypesOverride = node.isExplicitTypeDeclaration(); - return parseReferenceValueAsReference(itemName, node, definition, pc, allowMissingRefTypesOverride); // This is "real" reference (oid, and nothing more) + rv = parseReferenceValueAsReference(itemName, node, definition, pc, allowMissingRefTypesOverride); // This is "real" reference (oid, and nothing more) } + + addMetadataIfPresent(rv, node, pc); + return rv; } private PrismReferenceValue parseReferenceValueAsReference(QName name, @NotNull XNodeImpl xnode, @NotNull PrismReferenceDefinition definition, @@ -661,6 +678,7 @@ private PrismReferenceValue parseReferenceValueAsReference(QName name, @NotNull return refVal; } + @SuppressWarnings({ "unused", "SameParameterValue" }) private boolean isDefinedProperty(QName itemName, String parentNamespace, Set properties) { // TODO: Namespace awarness is disabled, because some calls from parseItem use ItemDefinition.itemName // instead of used itemName @@ -782,11 +800,7 @@ private ItemDefinition createDynamicItemDefinition(QName itemName, XNode node //TODO public ItemDefinition locateItemDefinition( - @NotNull PrismContainerDefinition containerDefinition, @NotNull QName itemName, @Nullable XNode xnode) - throws SchemaException { + @NotNull PrismContainerDefinition containerDefinition, @NotNull QName itemName, @Nullable XNode xnode) { return locateItemDefinition(itemName, containerDefinition.getComplexTypeDefinition(), xnode); } - - - } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaDescriptionParser.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaDescriptionParser.java index 02cb1858576..baa2e5cfafd 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaDescriptionParser.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaDescriptionParser.java @@ -11,6 +11,8 @@ import com.evolveum.midpoint.prism.schema.SchemaRegistry; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.util.exception.TunnelException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -98,17 +100,25 @@ static SchemaDescriptionImpl parseInputStream(InputStream input, String descript return desc; } - public static SchemaDescriptionImpl parseFile(File file) throws SchemaException { + public static SchemaDescriptionImpl parseFile(File file) throws SchemaException, IOException { SchemaDescriptionImpl desc = new SchemaDescriptionImpl("file " + file.getPath(), file.getPath()); - desc.setStreamable(() -> { - InputStream inputStream; - try { - inputStream = new FileInputStream(file); - } catch (FileNotFoundException e) { - throw new IllegalStateException("Cannot fetch file for schema " + file, e); + try { + desc.setStreamable(() -> { + InputStream inputStream; + try { + inputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new TunnelException("Cannot fetch file for schema " + file, e); + } + return inputStream; + }); + } catch (TunnelException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } else { + throw new SystemException(e.getCause()); } - return inputStream; - }); + } parseFromInputStream(desc); return desc; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaRegistryImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaRegistryImpl.java index 98f0aada38d..292df1572d2 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaRegistryImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaRegistryImpl.java @@ -7,19 +7,10 @@ package com.evolveum.midpoint.prism.impl.schema; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -79,14 +70,11 @@ */ public class SchemaRegistryImpl implements DebugDumpable, SchemaRegistry { - private static final QName DEFAULT_XSD_TYPE = DOMUtil.XSD_STRING; - private static final String DEFAULT_RUNTIME_CATALOG_RESOURCE = "META-INF/catalog-runtime.xml"; private File[] catalogFiles; // overrides catalog resource name private String catalogResourceName = DEFAULT_RUNTIME_CATALOG_RESOURCE; - private javax.xml.validation.SchemaFactory schemaFactory; private javax.xml.validation.Schema javaxSchema; private EntityResolver builtinSchemaResolver; final private List schemaDescriptions = new ArrayList<>(); @@ -98,17 +86,17 @@ public class SchemaRegistryImpl implements DebugDumpable, SchemaRegistry { private DynamicNamespacePrefixMapper namespacePrefixMapper; private String defaultNamespace; - private ConcurrentHashMap isListByXsiType = new ConcurrentHashMap<>(); - private ConcurrentHashMap isListByElementName = new ConcurrentHashMap<>(); - private ConcurrentHashMap> classForTypeIncludingXsd = new ConcurrentHashMap<>(); // TODO better name, probably unify with the latter - private ConcurrentHashMap> classForTypeExcludingXsd = new ConcurrentHashMap<>(); // TODO better name, probably unify with the former - private ConcurrentHashMap, PrismObjectDefinition> objectDefinitionForClass = new ConcurrentHashMap<>(); // experimental - private ConcurrentHashMap> objectDefinitionForType = new ConcurrentHashMap<>(); // experimental + private final ConcurrentHashMap isListByXsiType = new ConcurrentHashMap<>(); + private final ConcurrentHashMap isListByElementName = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> classForTypeIncludingXsd = new ConcurrentHashMap<>(); // TODO better name, probably unify with the latter + private final ConcurrentHashMap> classForTypeExcludingXsd = new ConcurrentHashMap<>(); // TODO better name, probably unify with the former + private final ConcurrentHashMap, PrismObjectDefinition> objectDefinitionForClass = new ConcurrentHashMap<>(); // experimental + private final ConcurrentHashMap> objectDefinitionForType = new ConcurrentHashMap<>(); // experimental private static final Class NO_CLASS = Void.class; private static final PrismObjectDefinition NO_OBJECT_DEFINITION = new DummyPrismObjectDefinition(); - private XmlEntityResolver entityResolver = new XmlEntityResolverImpl(this); + private final XmlEntityResolver entityResolver = new XmlEntityResolverImpl(this); @Autowired // TODO does this work? private PrismContext prismContext; @@ -117,6 +105,13 @@ public class SchemaRegistryImpl implements DebugDumpable, SchemaRegistry { private final Collection invalidationListeners = new ArrayList<>(); + private QName valueMetadataTypeName; + + // lazily evaluated (because we need be initialized to resolve value metadata type to the definition) + private PrismContainerDefinition valueMetadataDefinition; + + private static final QName DEFAULT_VALUE_METADATA_NAME = new QName("valueMetadata"); + @Override public DynamicNamespacePrefixMapper getNamespacePrefixMapper() { return namespacePrefixMapper; @@ -152,6 +147,7 @@ public EntityResolver getBuiltinSchemaResolver() { return builtinSchemaResolver; } + @SuppressWarnings("unused") // consider removal public File[] getCatalogFiles() { return catalogFiles; } @@ -160,6 +156,7 @@ public void setCatalogFiles(File[] catalogFiles) { this.catalogFiles = catalogFiles; } + @SuppressWarnings("unused") // consider removal public String getCatalogResourceName() { return catalogResourceName; } @@ -181,6 +178,7 @@ public void setDefaultNamespace(String defaultNamespace) { /** * Must be called before call to initialize() */ + @SuppressWarnings("unused") // consider removal public void registerSchemaResource(String resourcePath, String usualPrefix) throws SchemaException { SchemaDescriptionImpl desc = SchemaDescriptionParser.parseResource(resourcePath); desc.setUsualPrefix(usualPrefix); @@ -260,7 +258,7 @@ public void registerSchema(Node node, String sourceDescription) throws SchemaExc registerSchemaDescription(SchemaDescriptionParser.parseNode(node, sourceDescription)); } - public void registerPrismSchemaFile(File file) throws FileNotFoundException, SchemaException { + public void registerPrismSchemaFile(File file) throws IOException, SchemaException { loadPrismSchemaFileDescription(file); } @@ -268,16 +266,15 @@ public void registerPrismSchema(InputStream input, String sourceDescription) thr loadPrismSchemaDescription(input, sourceDescription); } - private SchemaDescriptionImpl loadPrismSchemaFileDescription(File file) throws SchemaException { + private void loadPrismSchemaFileDescription(File file) throws SchemaException, IOException { if (!(file.getName().matches(".*\\.xsd$"))){ LOGGER.trace("Skipping registering {}, because it is not schema definition.", file.getAbsolutePath()); - return null; + } else { + LOGGER.debug("Loading schema from file {}", file); + SchemaDescriptionImpl desc = SchemaDescriptionParser.parseFile(file); + desc.setPrismSchema(true); + registerSchemaDescription(desc); } - LOGGER.debug("Loading schema from file {}", file); - SchemaDescriptionImpl desc = SchemaDescriptionParser.parseFile(file); - desc.setPrismSchema(true); - registerSchemaDescription(desc); - return desc; } private void loadPrismSchemaDescription(InputStream input, String sourceDescription) @@ -303,12 +300,12 @@ private void registerSchemaDescription(SchemaDescriptionImpl desc) { invalidateCaches(); } - public void registerPrismSchemasFromDirectory(File directory) throws FileNotFoundException, SchemaException { + public void registerPrismSchemasFromDirectory(File directory) throws IOException, SchemaException { registerPrismSchemasFromDirectory(directory, emptyList()); } public void registerPrismSchemasFromDirectory(File directory, @NotNull Collection extensionFilesToIgnore) - throws FileNotFoundException, SchemaException { + throws IOException, SchemaException { File[] fileArray = directory.listFiles(); if (fileArray != null) { List files = Arrays.asList(fileArray); @@ -390,7 +387,7 @@ private void invalidateCaches() { } private void parseJavaxSchema() throws SAXException { - schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Source[] sources = new Source[schemaDescriptions.size()]; int i = 0; for (SchemaDescription schemaDescription : schemaDescriptions) { @@ -826,8 +823,10 @@ public T findItemDefinitionByFullPath(Class Class computeCompileTimeClass(QName typeName) { if (td == null) { return null; } + //noinspection unchecked return (Class) td.getCompileTimeClass(); } SchemaDescription desc = findSchemaDescriptionByNamespace(typeName.getNamespaceURI()); @@ -889,6 +889,7 @@ public Class getCompileTimeClassForObjectType(QName object @Override public PrismObjectDefinition determineDefinitionFromClass(Class compileTimeClass) { + //noinspection unchecked PrismObjectDefinition def = findObjectDefinitionByCompileTimeClass(compileTimeClass); if (def != null) { return def; @@ -1283,6 +1284,38 @@ private IsList determineIsListFromType(@NotNull QName xsiType) { } } + public synchronized void setValueMetadataTypeName(QName typeName) { + valueMetadataTypeName = typeName; + valueMetadataDefinition = null; + } + + @Override + @NotNull + public synchronized PrismContainerDefinition getValueMetadataDefinition() { + if (valueMetadataDefinition == null) { + valueMetadataDefinition = resolveValueMetadataDefinition(); + } + return valueMetadataDefinition; + } + + private PrismContainerDefinition resolveValueMetadataDefinition() { + if (!initialized) { + throw new IllegalStateException("Schema registry is not yet initialized"); + } + + if (valueMetadataTypeName != null) { + return Objects.requireNonNull( + findContainerDefinitionByType(valueMetadataTypeName), + () -> "no definition for value metadata type " + valueMetadataTypeName); + } else { + return createDefaultValueMetadataDefinition(); + } + } + + private PrismContainerDefinition createDefaultValueMetadataDefinition() { + return createAdHocContainerDefinition(DEFAULT_VALUE_METADATA_NAME, null, 0, 1); + } + //endregion //region TODO categorize diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/MapXNodeImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/MapXNodeImpl.java index 2707d9aaad6..2d510917521 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/MapXNodeImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/MapXNodeImpl.java @@ -48,6 +48,8 @@ public class MapXNodeImpl extends XNodeImpl implements MapXNode { private boolean hasDefaultNamespaceMarkers; + private MapXNode metadata; + public int size() { return subnodes.size(); } @@ -245,7 +247,8 @@ public boolean equals(Object o) { return false; } MapXNodeImpl other = (MapXNodeImpl) o; - return MiscUtil.unorderedCollectionEquals(this.subnodes.entrySet(), other.subnodes.entrySet()); + return MiscUtil.unorderedCollectionEquals(this.subnodes.entrySet(), other.subnodes.entrySet()) && + metadataEquals(this.metadata, other.metadata); } public int hashCode() { @@ -262,6 +265,7 @@ public int hashCode() { public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.debugDumpMapMultiLine(sb, this.toMap(), indent, true, dumpSuffix()); + appendMetadata(sb, indent, metadata); return sb.toString(); } @@ -366,4 +370,15 @@ public void replaceDefaultNamespaceMarkers(String marker, String defaultNamespac public void setHasDefaultNamespaceMarkers() { hasDefaultNamespaceMarkers = true; } + + + @Override + public MapXNode getMetadataNode() { + return metadata; + } + + @Override + public void setMetadataNode(MapXNode metadata) { + this.metadata = metadata; + } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/PrimitiveXNodeImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/PrimitiveXNodeImpl.java index f89c7782967..5ef2d81919e 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/PrimitiveXNodeImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/PrimitiveXNodeImpl.java @@ -20,6 +20,7 @@ import com.evolveum.midpoint.prism.util.JavaTypeConverter; import com.evolveum.midpoint.prism.impl.xml.XmlTypeConverterInternal; import com.evolveum.midpoint.prism.xml.XsdTypeMapper; +import com.evolveum.midpoint.prism.xnode.MapXNode; import com.evolveum.midpoint.prism.xnode.PrimitiveXNode; import com.evolveum.midpoint.prism.xnode.ValueParser; import com.evolveum.midpoint.prism.xnode.XNode; @@ -45,8 +46,11 @@ public class PrimitiveXNodeImpl extends XNodeImpl implements Serializable, Pr * - If value is non-null, super.typeName must be non-null. */ private T value; + private ValueParser valueParser; + private MapXNode metadata; + /** * If set to true then this primitive value either came from an attribute * or we prefer this to be represented as an attribute (if the target format @@ -234,6 +238,7 @@ public String debugDump(int indent) { if (dumpSuffix != null) { sb.append(dumpSuffix); } + appendMetadata(sb, indent, metadata); return sb.toString(); } @@ -323,18 +328,23 @@ public boolean equals(Object obj) { } PrimitiveXNodeImpl other = (PrimitiveXNodeImpl) obj; - if (other.isParsed() && isParsed()){ + + if (!metadataEquals(this.metadata, other.metadata)) { + return false; + } + + if (other.isParsed() && isParsed()) { return value.equals(other.value); - } else if (!other.isParsed() && !isParsed()){ + } else if (!other.isParsed() && !isParsed()) { // TODO consider problem with namespaces (if string value is QName/ItemPath its meaning can depend on namespace declarations that are placed outside the element) String thisStringVal = this.getStringValue(); String otherStringVal = other.getStringValue(); return Objects.equals(thisStringVal, otherStringVal); - } else if (other.isParsed() && !isParsed()){ + } else if (other.isParsed() && !isParsed()) { String thisStringValue = this.getStringValue(); String otherStringValue = String.valueOf(other.value); return Objects.equals(otherStringValue, thisStringValue); - } else if (!other.isParsed() && isParsed()){ + } else if (!other.isParsed() && isParsed()) { String thisStringValue = String.valueOf(value); String otherStringValue = other.getStringValue(); return Objects.equals(thisStringValue, otherStringValue); @@ -387,4 +397,14 @@ public void performFreeze() { } super.performFreeze(); } + + @Override + public MapXNode getMetadataNode() { + return metadata; + } + + @Override + public void setMetadataNode(MapXNode metadata) { + this.metadata = metadata; + } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/SchemaXNodeImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/SchemaXNodeImpl.java index 780ed641602..79c2960200f 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/SchemaXNodeImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/SchemaXNodeImpl.java @@ -6,6 +6,7 @@ */ package com.evolveum.midpoint.prism.impl.xnode; +import com.evolveum.midpoint.prism.xnode.MapXNode; import com.evolveum.midpoint.prism.xnode.SchemaXNode; import org.w3c.dom.Element; @@ -21,6 +22,8 @@ public class SchemaXNodeImpl extends XNodeImpl implements SchemaXNode { private Element schemaElement; + private MapXNode metadata; + public Element getSchemaElement() { return schemaElement; } @@ -52,6 +55,7 @@ public String debugDump(int indent) { if (dumpSuffix != null) { sb.append(dumpSuffix); } + appendMetadata(sb, indent, metadata); return sb.toString(); } @@ -67,6 +71,10 @@ public boolean equals(Object o) { SchemaXNodeImpl that = (SchemaXNodeImpl) o; + if (!metadataEquals(metadata, that.metadata)) { + return false; + } + if (schemaElement == null) { return that.schemaElement == null; } @@ -80,4 +88,14 @@ public boolean equals(Object o) { public int hashCode() { return 1; // the same as in DomAwareHashCodeStrategy } + + @Override + public MapXNode getMetadataNode() { + return metadata; + } + + @Override + public void setMetadataNode(MapXNode metadata) { + this.metadata = metadata; + } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java index cfcc66215c7..b076c210a0a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java @@ -12,7 +12,9 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.prism.AbstractFreezable; +import com.evolveum.midpoint.prism.xnode.MapXNode; import com.evolveum.midpoint.prism.xnode.XNode; +import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.Transformer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -36,7 +38,7 @@ public abstract class XNodeImpl extends AbstractFreezable implements XNode { public static final QName KEY_REFERENCE_TARGET_NAME = new QName(null, "targetName"); public static final QName KEY_REFERENCE_OBJECT = new QName(null, "object"); - public static final QName DUMMY_NAME = new QName(null, "dummy"); + private static final QName DUMMY_NAME = new QName(null, "dummy"); // Common fields protected XNodeImpl parent; // currently unused @@ -63,6 +65,11 @@ public abstract class XNodeImpl extends AbstractFreezable implements XNode { // a comment that could be stored into formats that support these (e.g. XML or YAML) private String comment; + /** + * Custom data to be used during parsing process. TODO reconsider + */ + private transient Object parserData; + public XNodeImpl getParent() { return parent; } @@ -227,4 +234,26 @@ public final boolean isSingleEntryMap() { return this instanceof MapXNodeImpl && ((MapXNodeImpl) this).size() == 1; } + public Object getParserData() { + return parserData; + } + + public void setParserData(Object parserData) { + this.parserData = parserData; + } + + void appendMetadata(StringBuilder sb, int indent, MapXNode metadata) { + if (metadata != null) { + sb.append("\n"); + DebugUtil.debugDumpWithLabel(sb, "metadata", metadata, indent); + } + } + + boolean metadataEquals(MapXNode metadata1, MapXNode metadata2) { + if (metadata1 == null) { + return metadata2 == null; + } else { + return metadata1.equals(metadata2); + } + } } diff --git a/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd b/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd index 99f3592c663..1ec2bc521ec 100644 --- a/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd +++ b/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd @@ -409,5 +409,16 @@ + + + + + + + + + + + + - diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java index f9a049ec8ed..808f9321c38 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java @@ -105,6 +105,8 @@ public class PrismInternalTestUtil implements PrismContextFactory { public static final String REF_WITH_FILTER_NO_OID_BASENAME = "ref-with-filter-no-oid"; public static final String REF_WITHOUT_FILTER_BASENAME = "ref-without-filter"; + public static final String USER_ALICE_METADATA_BASENAME = "user-alice-metadata"; + // Namespaces public static final String DEFAULT_NAMESPACE_PREFIX = "http://midpoint.evolveum.com/xml/ns"; public static final String NS_FOO = "http://midpoint.evolveum.com/xml/ns/test/foo-1.xsd"; @@ -148,6 +150,9 @@ public class PrismInternalTestUtil implements PrismContextFactory { public static final ItemPath USER_ASSIGNMENT_DESCRIPTION_PATH = ItemPath.create(USER_ASSIGNMENT_QNAME, USER_DESCRIPTION_QNAME); public static final ItemName ASSIGNMENT_TYPE_QNAME = new ItemName(NS_FOO,"AssignmentType"); public static final ItemName USER_ACCOUNT_CONSTRUCTION_QNAME = new ItemName(NS_FOO,"accountConstruction"); + + private static final ItemName FOO_VALUE_METADATA_TYPE_QNAME = new ItemName(NS_FOO, "FooValueMetadataType"); + public static final Long USER_ASSIGNMENT_1_ID = 1111L; public static final Long USER_ASSIGNMENT_2_ID = 1112L; public static final Long USER_ASSIGNMENT_3_ID = 1113L; @@ -226,13 +231,14 @@ public static PrismContext constructInitializedPrismContext(File extraSchema) th return context; } - public static PrismContextImpl constructPrismContext() throws SchemaException, FileNotFoundException { + public static PrismContextImpl constructPrismContext() throws SchemaException, IOException { return constructPrismContext(null); } - public static PrismContextImpl constructPrismContext(File extraSchema) throws SchemaException, FileNotFoundException { + public static PrismContextImpl constructPrismContext(File extraSchema) throws SchemaException, IOException { SchemaRegistryImpl schemaRegistry = new SchemaRegistryImpl(); schemaRegistry.setCatalogResourceName(TEST_CATALOG_RESOURCE_NAME); + schemaRegistry.setDefaultNamespace(NS_FOO); DynamicNamespacePrefixMapper prefixMapper = new GlobalDynamicNamespacePrefixMapper(); // Set default namespace? schemaRegistry.setNamespacePrefixMapper(prefixMapper); @@ -241,12 +247,13 @@ public static PrismContextImpl constructPrismContext(File extraSchema) throws Sc schemaRegistry.registerPrismSchemaResource("xml/ns/public/types-3.xsd", "t", com.evolveum.prism.xml.ns._public.types_3.ObjectFactory.class.getPackage()); schemaRegistry.registerPrismSchemaResource("xml/ns/public/query-3.xsd", "q", com.evolveum.prism.xml.ns._public.query_3.ObjectFactory.class.getPackage()); schemaRegistry.registerPrismSchemasFromDirectory(SCHEMA_DIR); - if (extraSchema != null){ + if (extraSchema != null) { schemaRegistry.registerPrismSchemaFile(extraSchema); } prefixMapper.registerPrefix(XMLConstants.W3C_XML_SCHEMA_NS_URI, DOMUtil.NS_W3C_XML_SCHEMA_PREFIX, false); prefixMapper.registerPrefix(PrismConstants.NS_ANNOTATION, PrismConstants.PREFIX_NS_ANNOTATION, false); prefixMapper.registerPrefix(PrismInternalTestUtil.NS_WEAPONS, PrismInternalTestUtil.NS_WEAPONS_PREFIX, false); + schemaRegistry.setValueMetadataTypeName(FOO_VALUE_METADATA_TYPE_QNAME); PrismContextImpl prismContext = PrismContextImpl.create(schemaRegistry); prismContext.setObjectsElementName(new QName("http://midpoint.evolveum.com/xml/ns/public/common/common-3", "objects")); return prismContext; @@ -256,7 +263,7 @@ public static PrismContextImpl constructPrismContext(File extraSchema) throws Sc * @see com.evolveum.midpoint.prism.PrismContextFactory#createPrismContext() */ @Override - public PrismContext createPrismContext() throws SchemaException, FileNotFoundException { + public PrismContext createPrismContext() throws SchemaException, IOException { return constructPrismContext(); } diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java index 3171fe04331..9dd5215f179 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java @@ -6,6 +6,7 @@ */ package com.evolveum.midpoint.prism; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; import static com.evolveum.midpoint.prism.PrismInternalTestUtil.*; @@ -22,8 +23,9 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; +import com.evolveum.midpoint.prism.xnode.*; + import org.testng.annotations.Test; -import org.xml.sax.SAXException; import com.evolveum.midpoint.prism.delta.DiffUtil; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -135,7 +137,7 @@ public void test220RoundTripObject() throws Exception { roundTrip(getFile(USER_JACK_OBJECT_BASENAME), false, false); } - private void roundTrip(File file, boolean expectFullPolyName, boolean withIncomplete) throws SchemaException, SAXException, IOException { + private void roundTrip(File file, boolean expectFullPolyName, boolean withIncomplete) throws SchemaException, IOException { // GIVEN PrismContext prismContext = getPrismContext(); @@ -386,6 +388,109 @@ public void test600AccountBarbossa() throws Exception { )), names); } + @Test + public void test700UserAliceMetadataOnXNodeLevel() throws Exception { + given(); + PrismContext prismContext = getPrismContext(); + + when(); + RootXNode root = prismContext.parserFor(getFile(USER_ALICE_METADATA_BASENAME)).parseToXNode(); + displayValue("alice xnode", root); + + assertAliceMetadata(root); + + assertAliceMetadata(testSerializeMetadata(root, PrismContext.LANG_XML)); + assertAliceMetadata(testSerializeMetadata(root, PrismContext.LANG_JSON)); + assertAliceMetadata(testSerializeMetadata(root, PrismContext.LANG_YAML)); + } + + @Test + public void test710UserAliceMetadataOnPrismValueLevel() throws Exception { + given(); + PrismContext prismContext = getPrismContext(); + + when(); + PrismObject alice = prismContext.parserFor(getFile(USER_ALICE_METADATA_BASENAME)).parse(); + displayValue("alice", alice); + + assertAliceMetadata(alice); + + assertAliceMetadata(testSerializeMetadata(alice, PrismContext.LANG_XML)); + assertAliceMetadata(testSerializeMetadata(alice, PrismContext.LANG_JSON)); + assertAliceMetadata(testSerializeMetadata(alice, PrismContext.LANG_YAML)); + } + + private void assertAliceMetadata(RootXNode alice) throws SchemaException { + MapXNode user = (MapXNode) alice.getSubnode(); + assertSingleMetadata(user, "test", "abc"); + + XNode name = user.get(UserType.F_NAME); + assertSingleMetadata(name, "loa", "high"); + ListXNode additionalNames = (ListXNode) user.get(UserType.F_ADDITIONAL_NAMES); + assertSingleMetadata(additionalNames.get(0), "loa", "medium"); + assertSingleMetadata(additionalNames.get(1), "loa", "low"); + + XNode extension = user.get(new QName("extension")); + XNode singleStringType = ((MapXNode) extension).get(new QName("singleStringType")); + assertSingleMetadata(singleStringType, "loa", "low"); + } + + private void assertSingleMetadata(XNode node, String name, String expected) throws SchemaException { + assertThat(node).isInstanceOf(MetadataAware.class); + MapXNode metadataNode = ((MetadataAware) node).getMetadataNode(); + assertThat(metadataNode).isNotNull(); + assertThat(metadataNode.size()).isEqualTo(1); + + QName key = metadataNode.getSingleSubEntry("").getKey(); + assertThat(key.getNamespaceURI()).isEqualTo(NS_FOO); + assertThat(key.getLocalPart()).isEqualTo(name); + + //noinspection unchecked + PrimitiveXNode value = (PrimitiveXNode) metadataNode.get(new QName(name)); + assertThat(value).isNotNull(); + String parsedValue = value.getParsedValue(DOMUtil.XSD_STRING, String.class); + assertThat(parsedValue).isEqualTo(expected); + } + + private void assertAliceMetadata(PrismObject alice) throws SchemaException { + assertSingleMetadata(alice.getValue(), "test", "abc"); + assertSingleMetadata(alice.findItem(UserType.F_NAME).getValue(), "loa", "high"); + assertSingleMetadata(alice.findItem(UserType.F_ADDITIONAL_NAMES).getValues().get(0), "loa", "medium"); + assertSingleMetadata(alice.findItem(UserType.F_ADDITIONAL_NAMES).getValues().get(1), "loa", "low"); + assertSingleMetadata(alice.findItem(ItemPath.create(UserType.F_EXTENSION, "singleStringType")).getValue(), "loa", "low"); + } + + private void assertSingleMetadata(PrismValue value, String name, String expected) { + ValueMetadata valueMetadata = value.getValueMetadata(); + assertThat(valueMetadata).isNotNull(); + assertThat(valueMetadata.size()).isEqualTo(1); + + Item item = valueMetadata.findItem(ItemPath.create(name)); + assertThat(item).isNotNull(); + assertThat(item.size()).isEqualTo(1); + assertThat((Object) item.getValue().getRealValue()).isEqualTo(expected); + } + + private RootXNode testSerializeMetadata(RootXNode original, String language) throws SchemaException { + PrismContext prismContext = getPrismContext(); + String serialized = prismContext.serializerFor(language).serialize(original); + displayValue("serialized", serialized); + + RootXNode reparsed = prismContext.parserFor(serialized).parseToXNode(); + displayValue("reparsed", reparsed); + return reparsed; + } + + private PrismObject testSerializeMetadata(PrismObject object, String language) throws SchemaException { + PrismContext prismContext = getPrismContext(); + String serialized = prismContext.serializerFor(language).serialize(object); + displayValue("serialized", serialized); + + PrismObject reparsed = prismContext.parserFor(serialized).parse(); + displayValue("reparsed", reparsed); + return reparsed; + } + protected void assertUserAdhoc(PrismObject user, boolean expectRawInConstructions, boolean withIncomplete) throws SchemaException { user.checkConsistence(); assertUserJackContent(user, expectRawInConstructions, true, withIncomplete); diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingJson.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingJson.java index 29447da4e8a..816507a4929 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingJson.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingJson.java @@ -6,8 +6,6 @@ */ package com.evolveum.midpoint.prism; -import org.testng.annotations.Test; - public class TestPrismParsingJson extends TestPrismParsing { @Override @@ -20,10 +18,6 @@ protected String getFilenameSuffix() { return "json"; } - @Test - public void f() { - } - @Override protected String getOutputFormat() { return PrismContext.LANG_JSON; diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractLexicalProcessorTest.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractLexicalProcessorTest.java index 53441fc3931..7d8a0ea49bc 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractLexicalProcessorTest.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractLexicalProcessorTest.java @@ -6,6 +6,7 @@ */ package com.evolveum.midpoint.prism.lex; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; import static com.evolveum.midpoint.prism.PrismInternalTestUtil.*; @@ -52,19 +53,19 @@ public abstract class AbstractLexicalProcessorTest extends AbstractPrismTest { private static final QName XSD_COMPLEX_TYPE_ELEMENT_NAME = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "complexType"); - public static final String EVENT_HANDLER_FILE_BASENAME = "event-handler"; - private static final String OBJECTS_1 = "objects-1"; - private static final String OBJECTS_3_NS = "objects-3-ns"; - private static final String OBJECTS_4_NO_ROOT_NS = "objects-4-no-root-ns"; - private static final String OBJECTS_5_ERROR = "objects-5-error"; - private static final String OBJECTS_6_SINGLE = "objects-6-single"; - private static final String OBJECTS_7_SINGLE = "objects-7-single"; + private static final String EVENT_HANDLER_FILE_BASENAME = "event-handler"; + private static final String OBJECTS_0_EMPTY = "objects-0-empty"; + private static final String OBJECTS_1_LIST = "objects-1-list"; + private static final String OBJECTS_2_SINGLE_NON_LIST = "objects-2-single-non-list"; + private static final String OBJECTS_3_SINGLE_LIST = "objects-3-single-list"; + + private static final String OBJECTS_4_ERROR = "objects-4-error"; protected abstract String getSubdirName(); protected abstract String getFilenameSuffix(); - protected File getCommonSubdir() { + private File getCommonSubdir() { return new File(COMMON_DIR_PATH, getSubdirName()); } @@ -72,12 +73,16 @@ protected File getFile(String baseName) { return new File(getCommonSubdir(), baseName + "." + getFilenameSuffix()); } - protected abstract LexicalProcessor createParser(); + protected abstract LexicalProcessor createLexicalProcessor(); + + ParserSource getFileSource(String basename) { + return new ParserFileSource(getFile(basename)); + } @Test public void testParseUserToPrism() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); PrismContext prismContext = PrismTestUtil.getPrismContext(); // WHEN (parse to xnode) @@ -95,17 +100,12 @@ public void testParseUserToPrism() throws Exception { assertUserJackXNodeOrdering("serialized xnode", xnode); assertUserJack(user, true, true); - - } - - ParserSource getFileSource(String basename) { - return new ParserFileSource(getFile(basename)); } @Test public void testParseUserRoundTrip() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); PrismContext prismContext = PrismTestUtil.getPrismContext(); // WHEN (parse) @@ -158,8 +158,10 @@ public void testParseUserRoundTrip() throws Exception { PrismObject accountRefObjOrig = findObjectFromAccountRef(user); PrismObject accountRefObjRe = findObjectFromAccountRef(reparsedUser); - ObjectDelta accountRefObjDiff = DiffUtil.diff(accountRefObjOrig, accountRefObjRe); + //noinspection unchecked + ObjectDelta accountRefObjDiff = DiffUtil.diff(accountRefObjOrig, accountRefObjRe); System.out.println("\naccountRef object diff:"); + assert accountRefObjDiff != null; System.out.println(accountRefObjDiff.debugDump()); assertTrue("Re-parsed object in accountRef does not match: " + accountRefObjDiff, accountRefObjDiff.isEmpty()); @@ -173,7 +175,7 @@ public void testParseUserRoundTrip() throws Exception { @Test public void testParseResourceRumToPrism() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); PrismContext prismContext = PrismTestUtil.getPrismContext(); // WHEN (parse to xnode) @@ -189,13 +191,12 @@ public void testParseResourceRumToPrism() throws Exception { System.out.println(resource.debugDump()); assertResourceRum(resource); - } @Test public void testParseResourceRoundTrip() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); PrismContext prismContext = PrismTestUtil.getPrismContext(); // WHEN (parse) @@ -269,28 +270,31 @@ private void assertResourceRum(PrismObject resource) throws Schema } private PrismObject findObjectFromAccountRef(PrismObject user) { - for (PrismReferenceValue rval : user.findReference(UserType.F_ACCOUNT_REF).getValues()) { - if (rval.getObject() != null) { - return rval.getObject(); + for (PrismReferenceValue refVal : user.findReference(UserType.F_ACCOUNT_REF).getValues()) { + if (refVal.getObject() != null) { + return refVal.getObject(); } } return null; } - protected X getAssertXNode(String message, XNode xnode, Class expectedClass) { + X getAssertXNode(String message, XNode xnode, Class expectedClass) { assertNotNull(message + " is null", xnode); assertTrue(message + ", expected " + expectedClass.getSimpleName() + ", was " + xnode.getClass().getSimpleName(), expectedClass.isAssignableFrom(xnode.getClass())); + //noinspection unchecked return (X) xnode; } - protected X getAssertXMapSubnode(String message, MapXNodeImpl xmap, QName key, Class expectedClass) { + @SuppressWarnings("SameParameterValue") + X getAssertXMapSubnode(String message, MapXNodeImpl xmap, QName key, Class expectedClass) { XNodeImpl xsubnode = xmap.get(key); assertNotNull(message + " no key " + key, xsubnode); return getAssertXNode(message + " key " + key, xsubnode, expectedClass); } - protected void assertUserJackXNodeOrdering(String message, XNode xnode) { + @SuppressWarnings("SameParameterValue") + private void assertUserJackXNodeOrdering(String message, XNode xnode) { if (xnode instanceof RootXNodeImpl) { xnode = ((RootXNodeImpl) xnode).getSubnode(); } @@ -321,7 +325,7 @@ protected void validateResourceSchema(String dataString, PrismContext prismConte @Test(enabled = false) public void testParseEventHandler() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); PrismContext prismContext = PrismTestUtil.getPrismContext(); // WHEN (parse to xnode) @@ -341,12 +345,38 @@ public void testParseEventHandler() throws Exception { System.out.println("XNode after unmarshalling and marshalling back:"); System.out.println(marshalled.debugDump()); + } + + @Test + public void testParseObjects_0_Empty() throws Exception { + given(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); + + when(); + List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_0_EMPTY), createDefaultParsingContext()); + + then(); + System.out.println("Parsed objects (standard way):"); + System.out.println(DebugUtil.debugDump(nodesStandard)); + + assertThat(nodesStandard).isEmpty(); + + testSerializeAndParseAgain(lexicalProcessor, nodesStandard); + } + + private void testSerializeAndParseAgain(LexicalProcessor lexicalProcessor, List nodes) + throws SchemaException, IOException { + String serialized = lexicalProcessor.write(nodes, null); + displayValue("Serialized", serialized); + List reparsed = lexicalProcessor.readObjects(new ParserStringSource(serialized), createDefaultParsingContext()); + displayValue("Re-parsed", reparsed); + assertThat(reparsed).isEqualTo(nodes); } @Test - public void testParseObjectsIteratively_1() throws Exception { - List nodes = standardTest(OBJECTS_1, 3); + public void testParseObjects_1_ListOfThree() throws Exception { + List nodes = executeReadObjectsTest(OBJECTS_1_LIST, 3); final String NS_C = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; nodes.forEach(n -> assertEquals("Wrong namespace", NS_C, n.getRootElementName().getNamespaceURI())); @@ -355,11 +385,10 @@ public void testParseObjectsIteratively_1() throws Exception { assertEquals("Wrong namespace for node 3", NS_C, getFirstElementNS(nodes, 2)); } - protected List standardTest(String fileName, int expectedCount) - throws SchemaException, IOException { + List executeReadObjectsTest(String fileName, int expectedCount) throws SchemaException, IOException { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); @@ -382,17 +411,19 @@ protected List standardTest(String fileName, int expectedCount) System.out.println(DebugUtil.debugDump(nodesStandard)); assertEquals("Nodes are different", nodesStandard, nodes); + + testSerializeAndParseAgain(lexicalProcessor, nodes); return nodes; } @Test - public void testParseObjectsIteratively_1_FirstTwo() throws Exception { + public void testParseObjects_1_ListOfThree_ReadFirstTwo() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_1), createDefaultParsingContext(), + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_1_LIST), createDefaultParsingContext(), node -> { nodes.add(node); return nodes.size() != 2; @@ -408,6 +439,8 @@ public void testParseObjectsIteratively_1_FirstTwo() throws Exception { nodes.forEach(n -> assertEquals("Wrong namespace", NS_C, n.getRootElementName().getNamespaceURI())); assertEquals("Wrong namespace for node 1", NS_C, getFirstElementNS(nodes, 0)); assertEquals("Wrong namespace for node 2", NS_C, getFirstElementNS(nodes, 1)); + + testSerializeAndParseAgain(lexicalProcessor, nodes); } String getFirstElementNS(List nodes, int index) { @@ -416,13 +449,13 @@ String getFirstElementNS(List nodes, int index) { } @Test - public void testParseObjectsIteratively_3_NS() throws Exception { + public void testParseObjects_2_SingleNonList() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_3_NS), createDefaultParsingContext(), + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_2_SINGLE_NON_LIST), createDefaultParsingContext(), node -> { nodes.add(node); return true; @@ -432,31 +465,31 @@ public void testParseObjectsIteratively_3_NS() throws Exception { System.out.println("Parsed objects (iteratively):"); System.out.println(DebugUtil.debugDump(nodes)); - assertEquals("Wrong # of nodes read", 3, nodes.size()); + assertEquals("Wrong # of nodes read", 1, nodes.size()); - nodes.forEach(n -> assertEquals("Wrong namespace", - "http://a/", n.getRootElementName().getNamespaceURI())); - assertEquals("Wrong namespace for node 1", "http://b/", getFirstElementNS(nodes, 0)); - assertEquals("Wrong namespace for node 2", "http://c/", getFirstElementNS(nodes, 1)); - assertEquals("Wrong namespace for node 3", "http://d/", getFirstElementNS(nodes, 2)); + final String NS_C = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; + nodes.forEach(n -> assertEquals("Wrong namespace", NS_C, n.getRootElementName().getNamespaceURI())); + assertEquals("Wrong namespace for node 1", NS_C, getFirstElementNS(nodes, 0)); // WHEN+THEN (parse in standard way) - List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_3_NS), createDefaultParsingContext()); + List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_2_SINGLE_NON_LIST), createDefaultParsingContext()); System.out.println("Parsed objects (standard way):"); System.out.println(DebugUtil.debugDump(nodesStandard)); assertEquals("Nodes are different", nodesStandard, nodes); + + testSerializeAndParseAgain(lexicalProcessor, nodes); } @Test - public void testParseObjectsIteratively_4_noRootNs() throws Exception { + public void testParseObjects_3_SingleList() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_4_NO_ROOT_NS), createDefaultParsingContext(), + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_3_SINGLE_LIST), createDefaultParsingContext(), node -> { nodes.add(node); return true; @@ -466,32 +499,32 @@ public void testParseObjectsIteratively_4_noRootNs() throws Exception { System.out.println("Parsed objects (iteratively):"); System.out.println(DebugUtil.debugDump(nodes)); - assertEquals("Wrong # of nodes read", 3, nodes.size()); + assertEquals("Wrong # of nodes read", 1, nodes.size()); - nodes.forEach(n -> assertEquals("Wrong namespace", - "", n.getRootElementName().getNamespaceURI())); - assertEquals("Wrong namespace for node 1", "http://b/", getFirstElementNS(nodes, 0)); - assertEquals("Wrong namespace for node 2", "http://c/", getFirstElementNS(nodes, 1)); - assertEquals("Wrong namespace for node 3", "http://d/", getFirstElementNS(nodes, 2)); + final String NS_C = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; + nodes.forEach(n -> assertEquals("Wrong namespace", NS_C, n.getRootElementName().getNamespaceURI())); + assertEquals("Wrong namespace for node 1", NS_C, getFirstElementNS(nodes, 0)); // WHEN+THEN (parse in standard way) - List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_4_NO_ROOT_NS), createDefaultParsingContext()); + List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_3_SINGLE_LIST), createDefaultParsingContext()); System.out.println("Parsed objects (standard way):"); System.out.println(DebugUtil.debugDump(nodesStandard)); assertEquals("Nodes are different", nodesStandard, nodes); + + testSerializeAndParseAgain(lexicalProcessor, nodes); } @Test - public void testParseObjectsIteratively_5_error() throws Exception { + public void testParseObjects_4_ErrorInSecondObject() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); try { - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_5_ERROR), createDefaultParsingContext(), + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_4_ERROR), createDefaultParsingContext(), node -> { nodes.add(node); return true; @@ -507,73 +540,9 @@ public void testParseObjectsIteratively_5_error() throws Exception { // WHEN+THEN (parse in standard way) try { - lexicalProcessor.readObjects(getFileSource(OBJECTS_5_ERROR), createDefaultParsingContext()); + lexicalProcessor.readObjects(getFileSource(OBJECTS_4_ERROR), createDefaultParsingContext()); } catch (Exception e) { // SchemaException for JSON/YAML, IllegalStateException for XML (do something about this) System.out.println("Got expected exception: " + e); } } - - @Test - public void testParseObjectsIteratively_6_single() throws Exception { - // GIVEN - LexicalProcessor lexicalProcessor = createParser(); - - // WHEN (parse to xnode) - List nodes = new ArrayList<>(); - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_6_SINGLE), createDefaultParsingContext(), - node -> { - nodes.add(node); - return true; - }); - - // THEN - System.out.println("Parsed objects (iteratively):"); - System.out.println(DebugUtil.debugDump(nodes)); - - assertEquals("Wrong # of nodes read", 1, nodes.size()); - - final String NS_C = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - nodes.forEach(n -> assertEquals("Wrong namespace", NS_C, n.getRootElementName().getNamespaceURI())); - assertEquals("Wrong namespace for node 1", NS_C, getFirstElementNS(nodes, 0)); - - // WHEN+THEN (parse in standard way) - List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_6_SINGLE), createDefaultParsingContext()); - - System.out.println("Parsed objects (standard way):"); - System.out.println(DebugUtil.debugDump(nodesStandard)); - - assertEquals("Nodes are different", nodesStandard, nodes); - } - - @Test - public void testParseObjectsIteratively_7_single() throws Exception { - // GIVEN - LexicalProcessor lexicalProcessor = createParser(); - - // WHEN (parse to xnode) - List nodes = new ArrayList<>(); - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_7_SINGLE), createDefaultParsingContext(), - node -> { - nodes.add(node); - return true; - }); - - // THEN - System.out.println("Parsed objects (iteratively):"); - System.out.println(DebugUtil.debugDump(nodes)); - - assertEquals("Wrong # of nodes read", 1, nodes.size()); - - final String NS_C = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - nodes.forEach(n -> assertEquals("Wrong namespace", NS_C, n.getRootElementName().getNamespaceURI())); - - // WHEN+THEN (parse in standard way) - List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_7_SINGLE), createDefaultParsingContext()); - - System.out.println("Parsed objects (standard way):"); - System.out.println(DebugUtil.debugDump(nodesStandard)); - - assertEquals("Nodes are different", nodesStandard, nodes); - } - } diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractJsonLexicalProcessorTest.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/DelegatingLexicalProcessorTest.java similarity index 64% rename from infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractJsonLexicalProcessorTest.java rename to infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/DelegatingLexicalProcessorTest.java index 74d9c70e0a3..6bd1a1dfb2e 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/AbstractJsonLexicalProcessorTest.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/DelegatingLexicalProcessorTest.java @@ -22,22 +22,23 @@ import com.evolveum.midpoint.util.exception.SchemaException; @SuppressWarnings("Duplicates") -public abstract class AbstractJsonLexicalProcessorTest extends AbstractLexicalProcessorTest { +public abstract class DelegatingLexicalProcessorTest extends AbstractLexicalProcessorTest { + + private static final String OBJECTS_JSON_YAML_1_INCOMPLETE_LIST = "objects-json-yaml-1-incomplete-list"; - private static final String OBJECTS_2_WRONG = "objects-2-wrong"; private static final String OBJECTS_2_WRONG_2 = "objects-2-wrong-2"; private static final String OBJECTS_9_LIST_SINGLE = "objects-9-list-single"; private static final String OBJECTS_10_LIST_OF_LISTS = "objects-10-list-of-lists"; @Test - public void testParseObjectsIteratively_2_Wrong() throws Exception { + public void testParseObjects_1_IncompleteList() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); try { - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_2_WRONG), PrismTestUtil.createDefaultParsingContext(), + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_JSON_YAML_1_INCOMPLETE_LIST), PrismTestUtil.createDefaultParsingContext(), node -> { nodes.add(node); return true; @@ -51,28 +52,13 @@ public void testParseObjectsIteratively_2_Wrong() throws Exception { System.out.println("Parsed objects (iteratively):"); System.out.println(DebugUtil.debugDump(nodes)); - assertEquals("Wrong # of nodes read", 3, nodes.size()); - - nodes.forEach(n -> assertEquals("Wrong namespace", "", n.getRootElementName().getNamespaceURI())); - assertEquals("Wrong namespace for node 1", "", getFirstElementNS(nodes, 0)); - assertEquals("Wrong namespace for node 2", "", getFirstElementNS(nodes, 1)); - assertEquals("Wrong namespace for node 3", "", getFirstElementNS(nodes, 2)); - - // WHEN+THEN (parse in standard way) - List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_2_WRONG), PrismTestUtil - .createDefaultParsingContext()); - - System.out.println("Parsed objects (standard way):"); - System.out.println(DebugUtil.debugDump(nodesStandard)); - - assertThat(nodesStandard).withFailMessage("Nodes are not different") - .isNotEqualTo(nodes); + assertEquals("Wrong # of nodes read", 2, nodes.size()); } - @Test + @Test(enabled = false) public void testParseObjectsIteratively_2_Wrong_2() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); @@ -94,13 +80,13 @@ public void testParseObjectsIteratively_2_Wrong_2() throws Exception { assertEquals("Wrong # of nodes read", 3, nodes.size()); } - @Test + @Test(enabled = false) public void testParseObjectsIteratively_9_listSingle() throws Exception { - standardTest(OBJECTS_9_LIST_SINGLE, 1); + executeReadObjectsTest(OBJECTS_9_LIST_SINGLE, 1); } - @Test + @Test(enabled = false) public void testParseObjectsIteratively_10_listOfLists() throws Exception { - standardTest(OBJECTS_10_LIST_OF_LISTS, 3); + executeReadObjectsTest(OBJECTS_10_LIST_OF_LISTS, 3); } } diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestDomParser.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestDomParser.java index 21131501d34..3cf6d6e4ea4 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestDomParser.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestDomParser.java @@ -11,9 +11,14 @@ import static org.testng.AssertJUnit.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import com.evolveum.midpoint.prism.ParserFileSource; +import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; +import com.evolveum.midpoint.util.DebugUtil; + import org.testng.annotations.Test; import org.xml.sax.SAXException; @@ -32,6 +37,9 @@ */ public class TestDomParser extends AbstractLexicalProcessorTest { + private static final String OBJECTS_XML_1_NO_NS = "objects-xml-1-no-ns"; + private static final String OBJECTS_XML_2_NS = "objects-xml-2-ns"; + @Override protected String getSubdirName() { return "xml"; @@ -43,14 +51,14 @@ protected String getFilenameSuffix() { } @Override - protected DomLexicalProcessor createParser() { + protected DomLexicalProcessor createLexicalProcessor() { return new DomLexicalProcessor(PrismTestUtil.getSchemaRegistry()); } @Test public void testParseUserToXNode() throws Exception { // GIVEN - DomLexicalProcessor parser = createParser(); + DomLexicalProcessor parser = createLexicalProcessor(); // WHEN XNodeImpl xnode = parser.read(new ParserFileSource(getFile(USER_JACK_FILE_BASENAME)), createDefaultParsingContext()); @@ -73,6 +81,75 @@ public void testParseUserToXNode() throws Exception { } + @Test + public void testParseObjects_xml_1_NoNs() throws Exception { + // GIVEN + LexicalProcessor lexicalProcessor = createLexicalProcessor(); + + // WHEN (parse to xnode) + List nodes = new ArrayList<>(); + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_XML_1_NO_NS), createDefaultParsingContext(), + node -> { + nodes.add(node); + return true; + }); + + // THEN + System.out.println("Parsed objects (iteratively):"); + System.out.println(DebugUtil.debugDump(nodes)); + + assertEquals("Wrong # of nodes read", 3, nodes.size()); + + nodes.forEach(n -> assertEquals("Wrong namespace", + "", n.getRootElementName().getNamespaceURI())); + assertEquals("Wrong namespace for node 1", "http://b/", getFirstElementNS(nodes, 0)); + assertEquals("Wrong namespace for node 2", "http://c/", getFirstElementNS(nodes, 1)); + assertEquals("Wrong namespace for node 3", "http://d/", getFirstElementNS(nodes, 2)); + + // WHEN+THEN (parse in standard way) + List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_XML_1_NO_NS), createDefaultParsingContext()); + + System.out.println("Parsed objects (standard way):"); + System.out.println(DebugUtil.debugDump(nodesStandard)); + + assertEquals("Nodes are different", nodesStandard, nodes); + } + + @Test + public void testParseObjects_xml_2_Ns() throws Exception { + // GIVEN + LexicalProcessor lexicalProcessor = createLexicalProcessor(); + + // WHEN (parse to xnode) + List nodes = new ArrayList<>(); + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_XML_2_NS), createDefaultParsingContext(), + node -> { + nodes.add(node); + return true; + }); + + // THEN + System.out.println("Parsed objects (iteratively):"); + System.out.println(DebugUtil.debugDump(nodes)); + + assertEquals("Wrong # of nodes read", 3, nodes.size()); + + nodes.forEach(n -> assertEquals("Wrong namespace", + "http://a/", n.getRootElementName().getNamespaceURI())); + assertEquals("Wrong namespace for node 1", "http://b/", getFirstElementNS(nodes, 0)); + assertEquals("Wrong namespace for node 2", "http://c/", getFirstElementNS(nodes, 1)); + assertEquals("Wrong namespace for node 3", "http://d/", getFirstElementNS(nodes, 2)); + + // WHEN+THEN (parse in standard way) + List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_XML_2_NS), createDefaultParsingContext()); + + System.out.println("Parsed objects (standard way):"); + System.out.println(DebugUtil.debugDump(nodesStandard)); + + assertEquals("Nodes are different", nodesStandard, nodes); + } + + private void validateSchemaCompliance(String xmlString, PrismContext prismContext) throws SAXException, IOException { // Document xmlDocument = DOMUtil.parseDocument(xmlString); // Schema javaxSchema = prismContext.getSchemaRegistry().getJavaxSchema(); diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestJsonParser.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestJsonParser.java index 762045f1059..65d3298377c 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestJsonParser.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestJsonParser.java @@ -6,10 +6,13 @@ */ package com.evolveum.midpoint.prism.lex; -import com.evolveum.midpoint.prism.impl.lex.json.JsonLexicalProcessor; +import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; +import com.evolveum.midpoint.prism.impl.lex.json.DelegatingLexicalProcessor; +import com.evolveum.midpoint.prism.impl.lex.json.reader.JsonReader; +import com.evolveum.midpoint.prism.impl.lex.json.writer.JsonWriter; import com.evolveum.midpoint.prism.util.PrismTestUtil; -public class TestJsonParser extends AbstractJsonLexicalProcessorTest { +public class TestJsonParser extends DelegatingLexicalProcessorTest { @Override protected String getSubdirName() { @@ -22,8 +25,10 @@ protected String getFilenameSuffix() { } @Override - protected JsonLexicalProcessor createParser() { - return new JsonLexicalProcessor(PrismTestUtil.getSchemaRegistry()); + protected LexicalProcessor createLexicalProcessor() { + return new DelegatingLexicalProcessor( + new JsonReader(PrismTestUtil.getSchemaRegistry()), + new JsonWriter()); } @Override diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestYamlParser.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestYamlParser.java index 39fac145d7c..e7ac64a157c 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestYamlParser.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/lex/TestYamlParser.java @@ -7,7 +7,9 @@ package com.evolveum.midpoint.prism.lex; import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; -import com.evolveum.midpoint.prism.impl.lex.json.YamlLexicalProcessor; +import com.evolveum.midpoint.prism.impl.lex.json.*; +import com.evolveum.midpoint.prism.impl.lex.json.reader.YamlReader; +import com.evolveum.midpoint.prism.impl.lex.json.writer.YamlWriter; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; import com.evolveum.midpoint.util.DebugUtil; @@ -20,9 +22,9 @@ import static com.evolveum.midpoint.prism.util.PrismTestUtil.createDefaultParsingContext; import static org.testng.AssertJUnit.assertEquals; -public class TestYamlParser extends AbstractJsonLexicalProcessorTest { +public class TestYamlParser extends DelegatingLexicalProcessorTest { - private static final String OBJECTS_8_MULTI_DOCUMENT = "objects-8-multi-document"; + private static final String OBJECTS_YAML_1_MULTI_DOCUMENT = "objects-yaml-1-multi-document"; @Override protected String getSubdirName() { @@ -35,8 +37,10 @@ protected String getFilenameSuffix() { } @Override - protected YamlLexicalProcessor createParser() { - return new YamlLexicalProcessor(PrismTestUtil.getSchemaRegistry()); + protected LexicalProcessor createLexicalProcessor() { + return new DelegatingLexicalProcessor( + new YamlReader(PrismTestUtil.getSchemaRegistry()), + new YamlWriter()); } @Override @@ -45,13 +49,13 @@ protected String getWhenItemSerialized() { } @Test - public void testParseObjectsIteratively_8_multiDocument() throws Exception { + public void testParseObjects_yaml_1_MultiDocument() throws Exception { // GIVEN - LexicalProcessor lexicalProcessor = createParser(); + LexicalProcessor lexicalProcessor = createLexicalProcessor(); // WHEN (parse to xnode) List nodes = new ArrayList<>(); - lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_8_MULTI_DOCUMENT), createDefaultParsingContext(), + lexicalProcessor.readObjectsIteratively(getFileSource(OBJECTS_YAML_1_MULTI_DOCUMENT), createDefaultParsingContext(), node -> { nodes.add(node); return true; @@ -71,12 +75,11 @@ public void testParseObjectsIteratively_8_multiDocument() throws Exception { assertEquals("Wrong namespace for node 4", "http://a/", i.next().getRootElementName().getNamespaceURI()); // WHEN+THEN (parse in standard way) - List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_8_MULTI_DOCUMENT), createDefaultParsingContext()); + List nodesStandard = lexicalProcessor.readObjects(getFileSource(OBJECTS_YAML_1_MULTI_DOCUMENT), createDefaultParsingContext()); System.out.println("Parsed objects (standard way):"); System.out.println(DebugUtil.debugDump(nodesStandard)); assertEquals("Nodes are different", nodesStandard, nodes); } - } diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java index 3b3e3b42dc0..2345e44cd06 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java @@ -73,18 +73,17 @@ public void testFilterUserNameJaxb() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - - System.out.println("Serialized filter (JAXB->DOM)"); - System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); - - DomAsserts.assertElementQName(filterClauseElement, EqualFilter.ELEMENT_NAME); - DomAsserts.assertSubElements(filterClauseElement, 2); - - DomAsserts.assertSubElement(filterClauseElement, PrismConstants.Q_VALUE); - Element valueElement = DOMUtil.getChildElement(filterClauseElement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(valueElement, "jack"); - +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); +// +// DomAsserts.assertElementQName(filterClauseElement, EqualFilter.ELEMENT_NAME); +// DomAsserts.assertSubElements(filterClauseElement, 2); +// +// DomAsserts.assertSubElement(filterClauseElement, PrismConstants.Q_VALUE); +// Element valueElement = DOMUtil.getChildElement(filterClauseElement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(valueElement, "jack"); } @Test @@ -122,22 +121,22 @@ public void testFilterUserAndJaxb() throws Exception { ListXNodeImpl xequalsList = (ListXNodeImpl) xandmap.get(EqualFilter.ELEMENT_NAME); PrismAsserts.assertSize(xequalsList, 2); - Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); - System.out.println("Serialized filter (JAXB->DOM)"); - System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); - - DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); - DomAsserts.assertSubElements(filterClauseElement, 2); - - Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); - DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); - Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(firstValueElement, "Jack"); - - Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); - DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); - Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(secondValueElement, "Caribbean"); +// Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); +// System.out.println("Serialized filter (JAXB->DOM)"); +// System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); +// +// DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); +// DomAsserts.assertSubElements(filterClauseElement, 2); +// +// Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); +// DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); +// Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(firstValueElement, "Jack"); +// +// Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); +// DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); +// Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(secondValueElement, "Caribbean"); } @Test @@ -159,15 +158,15 @@ public void testFilterTypeUserNone() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); - - System.out.println("Serialized filter (JAXB->DOM)"); - String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); - System.out.println(filterAsString); - logger.info(filterAsString); - - DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "type")); +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); +// System.out.println(filterAsString); +// logger.info(filterAsString); +// +// DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "type")); } @Test @@ -189,17 +188,17 @@ public void testFilterNotInOid() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); - - System.out.println("Serialized filter (JAXB->DOM)"); - String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); - System.out.println(filterAsString); - logger.info(filterAsString); - - DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); - assertEquals("wrong # of inOid subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "inOid").getLength()); - assertEquals("wrong # of value subfilters", 4, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); +// System.out.println(filterAsString); +// logger.info(filterAsString); +// +// DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); +// assertEquals("wrong # of inOid subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "inOid").getLength()); +// assertEquals("wrong # of value subfilters", 4, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); } @Test @@ -221,17 +220,17 @@ public void testFilterNotFullText() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); - - System.out.println("Serialized filter (JAXB->DOM)"); - String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); - System.out.println(filterAsString); - logger.info(filterAsString); - - DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); - assertEquals("wrong # of fullText subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "fullText").getLength()); - assertEquals("wrong # of value subfilters", 2, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); +// System.out.println(filterAsString); +// logger.info(filterAsString); +// +// DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); +// assertEquals("wrong # of fullText subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "fullText").getLength()); +// assertEquals("wrong # of value subfilters", 2, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); } private ObjectQuery toObjectQuery(Class type, SearchFilterType filterType) throws Exception { diff --git a/infra/prism-impl/src/test/resources/common/json/objects-0-empty.json b/infra/prism-impl/src/test/resources/common/json/objects-0-empty.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/json/objects-0-empty.json @@ -0,0 +1 @@ +[] diff --git a/infra/prism-impl/src/test/resources/common/json/objects-1.json b/infra/prism-impl/src/test/resources/common/json/objects-1-list.json similarity index 100% rename from infra/prism-impl/src/test/resources/common/json/objects-1.json rename to infra/prism-impl/src/test/resources/common/json/objects-1-list.json diff --git a/infra/prism-impl/src/test/resources/common/json/objects-10-list-of-lists.json b/infra/prism-impl/src/test/resources/common/json/objects-10-list-of-lists.json deleted file mode 100644 index 37db36842ac..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-10-list-of-lists.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - [ - { "user" : { "name" : "u1" } } - ], - [ - [ - [ - { "user" : { "name" : "u2" } } - ] - ], - [ - [ - [ - { "user" : { "name" : "u3" } } - ] - ] - ] - ] -] diff --git a/infra/prism-impl/src/test/resources/common/json/objects-6-single.json b/infra/prism-impl/src/test/resources/common/json/objects-2-single-non-list.json similarity index 100% rename from infra/prism-impl/src/test/resources/common/json/objects-6-single.json rename to infra/prism-impl/src/test/resources/common/json/objects-2-single-non-list.json diff --git a/infra/prism-impl/src/test/resources/common/json/objects-2-wrong.json b/infra/prism-impl/src/test/resources/common/json/objects-2-wrong.json deleted file mode 100644 index e628e30ce0e..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-2-wrong.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "objects" : [ { - "user" : { - "name" : "u1" - } - }, { - "user" : { - "name" : "u2" - } - }, { - "role" : { - "name" : "r1" - } - } ], - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -} diff --git a/infra/prism-impl/src/test/resources/common/json/objects-3-ns.json b/infra/prism-impl/src/test/resources/common/json/objects-3-ns.json deleted file mode 100644 index 5a0d2e8f916..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-3-ns.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "objects" : [ { - "http://a/#user" : { - "name" : "u1", - "@ns" : "http://b/" - } - }, { - "http://a/#user" : { - "name" : "u2", - "@ns" : "http://c/" - } - }, { - "http://a/#role" : { - "name" : "r1", - "@ns" : "http://d/" - } - } ] -} diff --git a/infra/prism-impl/src/test/resources/common/json/objects-3-single-list.json b/infra/prism-impl/src/test/resources/common/json/objects-3-single-list.json new file mode 100644 index 00000000000..e83b80e8d21 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/json/objects-3-single-list.json @@ -0,0 +1,10 @@ +[ + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user" : { + "name" : [ "u1", "u2" ], + "givenName" : [ "g1", "g2" ], + "familyName" : "f1" + } + } +] diff --git a/infra/prism-impl/src/test/resources/common/json/objects-2-wrong-2.json b/infra/prism-impl/src/test/resources/common/json/objects-4-error.json similarity index 68% rename from infra/prism-impl/src/test/resources/common/json/objects-2-wrong-2.json rename to infra/prism-impl/src/test/resources/common/json/objects-4-error.json index 7b4983028fd..bd8f5934838 100644 --- a/infra/prism-impl/src/test/resources/common/json/objects-2-wrong-2.json +++ b/infra/prism-impl/src/test/resources/common/json/objects-4-error.json @@ -1,16 +1,15 @@ -{ - "objects" : [ { +[ { "user" : { "name" : "u1" } }, { "user" : { "name" : "u2" + dsae41io432io432io43i2o43i2432 this is an error } }, { "role" : { "name" : "r1" } - } ], - "other" : "abcdef" -} + } +] diff --git a/infra/prism-impl/src/test/resources/common/json/objects-4-no-root-ns.json b/infra/prism-impl/src/test/resources/common/json/objects-4-no-root-ns.json deleted file mode 100644 index 2b8bc197bdc..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-4-no-root-ns.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "objects" : [ { - "user" : { - "name" : "u1", - "@ns" : "http://b/" - } - }, { - "user" : { - "name" : "u2", - "@ns" : "http://c/" - } - }, { - "role" : { - "name" : "r1", - "@ns" : "http://d/" - } - } ] -} diff --git a/infra/prism-impl/src/test/resources/common/json/objects-5-error.json b/infra/prism-impl/src/test/resources/common/json/objects-5-error.json deleted file mode 100644 index 779eb8bf92b..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-5-error.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "user" : { - "name" : "u1" - } - }, { - "user" : { - "name" : "u2" - dsae41io432io432io43i2o43i2432 this is an error - } - }, { - "role" : { - "name" : "r1" - } - } ] -} diff --git a/infra/prism-impl/src/test/resources/common/json/objects-7-single.json b/infra/prism-impl/src/test/resources/common/json/objects-7-single.json deleted file mode 100644 index 257e7bb0af0..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-7-single.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "user" : [ { - "name" : "u1", - "givenName" : "g1", - "familyName" : "f1" - } ] -} diff --git a/infra/prism-impl/src/test/resources/common/json/objects-9-list-single.json b/infra/prism-impl/src/test/resources/common/json/objects-9-list-single.json deleted file mode 100644 index ad428264e30..00000000000 --- a/infra/prism-impl/src/test/resources/common/json/objects-9-list-single.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "user" : { - "name" : "u1", - "givenName" : "g1", - "familyName" : "f1" - } - } -] diff --git a/infra/prism-impl/src/test/resources/common/json/objects-json-yaml-1-incomplete-list.json b/infra/prism-impl/src/test/resources/common/json/objects-json-yaml-1-incomplete-list.json new file mode 100644 index 00000000000..9c8c89a6186 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/json/objects-json-yaml-1-incomplete-list.json @@ -0,0 +1,15 @@ +[ + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user" : { + "name" : "u1" + } + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user" : { + "name" : "u2" + } + } + , +] diff --git a/infra/prism-impl/src/test/resources/common/json/user-alice-metadata.json b/infra/prism-impl/src/test/resources/common/json/user-alice-metadata.json new file mode 100644 index 00000000000..f1c6fd11824 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/json/user-alice-metadata.json @@ -0,0 +1,37 @@ +{ + "@ns" : "http://midpoint.evolveum.com/xml/ns/test/foo-1.xsd", + "user" : { + "@metadata" : { + "test" : "abc" + }, + "oid" : "f6ba0f28-8875-4b41-9915-1277ed561d11", + "version" : "42", + "name" : { + "@value" : "alice", + "@metadata" : { + "loa" : "high" + } + }, + "additionalNames" : [ { + "@value" : "Jane", + "@metadata" : { + "loa" : "medium" + } + }, { + "@value" : "Catherine", + "@metadata" : { + "loa" : "low" + } + } ], + "extension" : { + "@ns" : "http://midpoint.evolveum.com/xml/ns/test/extension", + "singleStringType" : { + "@ns" : "http://midpoint.evolveum.com/xml/ns/test/foo-1.xsd", + "@value" : "foobar", + "@metadata" : { + "loa" : "low" + } + } + } + } +} diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-0-empty.xml b/infra/prism-impl/src/test/resources/common/xml/objects-0-empty.xml new file mode 100644 index 00000000000..be0578c818f --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/xml/objects-0-empty.xml @@ -0,0 +1,9 @@ + + + + diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-1.xml b/infra/prism-impl/src/test/resources/common/xml/objects-1-list.xml similarity index 100% rename from infra/prism-impl/src/test/resources/common/xml/objects-1.xml rename to infra/prism-impl/src/test/resources/common/xml/objects-1-list.xml diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-6-single.xml b/infra/prism-impl/src/test/resources/common/xml/objects-2-single-non-list.xml similarity index 100% rename from infra/prism-impl/src/test/resources/common/xml/objects-6-single.xml rename to infra/prism-impl/src/test/resources/common/xml/objects-2-single-non-list.xml diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-3-single-list.xml b/infra/prism-impl/src/test/resources/common/xml/objects-3-single-list.xml new file mode 100644 index 00000000000..9adc9b4fd2a --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/xml/objects-3-single-list.xml @@ -0,0 +1,16 @@ + + + + + u1 + u2 + g1 + g2 + f1 + + diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-5-error.xml b/infra/prism-impl/src/test/resources/common/xml/objects-4-error.xml similarity index 100% rename from infra/prism-impl/src/test/resources/common/xml/objects-5-error.xml rename to infra/prism-impl/src/test/resources/common/xml/objects-4-error.xml diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-4-no-root-ns.json b/infra/prism-impl/src/test/resources/common/xml/objects-4-no-root-ns.json deleted file mode 100644 index 2b8bc197bdc..00000000000 --- a/infra/prism-impl/src/test/resources/common/xml/objects-4-no-root-ns.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "objects" : [ { - "user" : { - "name" : "u1", - "@ns" : "http://b/" - } - }, { - "user" : { - "name" : "u2", - "@ns" : "http://c/" - } - }, { - "role" : { - "name" : "r1", - "@ns" : "http://d/" - } - } ] -} diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-5-error.json b/infra/prism-impl/src/test/resources/common/xml/objects-5-error.json deleted file mode 100644 index 779eb8bf92b..00000000000 --- a/infra/prism-impl/src/test/resources/common/xml/objects-5-error.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "user" : { - "name" : "u1" - } - }, { - "user" : { - "name" : "u2" - dsae41io432io432io43i2o43i2432 this is an error - } - }, { - "role" : { - "name" : "r1" - } - } ] -} diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-7-single.xml b/infra/prism-impl/src/test/resources/common/xml/objects-7-single.xml deleted file mode 100644 index 1d4ac524d62..00000000000 --- a/infra/prism-impl/src/test/resources/common/xml/objects-7-single.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - u1 - g1 - f1 - diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-4-no-root-ns.xml b/infra/prism-impl/src/test/resources/common/xml/objects-xml-1-no-ns.xml similarity index 100% rename from infra/prism-impl/src/test/resources/common/xml/objects-4-no-root-ns.xml rename to infra/prism-impl/src/test/resources/common/xml/objects-xml-1-no-ns.xml diff --git a/infra/prism-impl/src/test/resources/common/xml/objects-3-ns.xml b/infra/prism-impl/src/test/resources/common/xml/objects-xml-2-ns.xml similarity index 100% rename from infra/prism-impl/src/test/resources/common/xml/objects-3-ns.xml rename to infra/prism-impl/src/test/resources/common/xml/objects-xml-2-ns.xml diff --git a/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml b/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml new file mode 100644 index 00000000000..6339a73d66c --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml @@ -0,0 +1,48 @@ + + + + <_metadata> + abc + + + <_value>alice + <_metadata> + high + + + + <_value>Jane + <_metadata> + medium + + + + <_value>Catherine + <_metadata> + low + + + + + <_value>foobar + <_metadata> + low + + + + diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-0-empty.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-0-empty.yaml new file mode 100644 index 00000000000..fe925944368 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/yaml/objects-0-empty.yaml @@ -0,0 +1,2 @@ +--- +[] diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-1-list.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-1-list.yaml new file mode 100644 index 00000000000..1ddbcf53d56 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/yaml/objects-1-list.yaml @@ -0,0 +1,13 @@ +--- +- + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: "u1" +- + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: "u2" +- + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + role: + name: "r1" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-1.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-1.yaml deleted file mode 100644 index d0722c5f607..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-1.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -user: - name: "u1" ---- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -user: - name: "u2" ---- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -role: - name: "r1" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-10-list-of-lists.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-10-list-of-lists.yaml deleted file mode 100644 index bd67a61d98c..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-10-list-of-lists.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- - - user: { name : u1 } - - - - - - user: { name : u2 } - - - - - - user: { name : u3 } diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-6-single.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-2-single-non-list.yaml similarity index 100% rename from infra/prism-impl/src/test/resources/common/yaml/objects-6-single.yaml rename to infra/prism-impl/src/test/resources/common/yaml/objects-2-single-non-list.yaml diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-2-wrong-2.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-2-wrong-2.yaml deleted file mode 100644 index 2d5b74e9c9b..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-2-wrong-2.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -objects: -- user: - name: "u1" -- user: - name: "u2" -- role: - name: "r1" -other: abcdefgh diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-2-wrong.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-2-wrong.yaml deleted file mode 100644 index c3cf2bf214c..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-2-wrong.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -objects: -- user: - name: "u1" -- user: - name: "u2" -- role: - name: "r1" -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-3-ns.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-3-ns.yaml deleted file mode 100644 index 9b0a6543a58..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-3-ns.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -objects: -- http://a/#user: - name: "u1" - '@ns': "http://b/" -- http://a/#user: - name: "u2" - '@ns': "http://c/" -- http://a/#role: - name: "r1" - '@ns': "http://d/" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-3-single-list.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-3-single-list.yaml new file mode 100644 index 00000000000..5f6ca90c0ad --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/yaml/objects-3-single-list.yaml @@ -0,0 +1,11 @@ +--- +- + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: + - "u1" + - "u2" + givenName: + - "g1" + - "g2" + familyName: "f1" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-5-error.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-4-error.yaml similarity index 79% rename from infra/prism-impl/src/test/resources/common/yaml/objects-5-error.yaml rename to infra/prism-impl/src/test/resources/common/yaml/objects-4-error.yaml index 56e1862df34..a61ed4c840c 100644 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-5-error.yaml +++ b/infra/prism-impl/src/test/resources/common/yaml/objects-4-error.yaml @@ -1,11 +1,13 @@ --- -objects: -- user: +- + user: name: "u1" -- user: +- + user: '@ns': http://a/ name: "u2" @#@!#@!#@ # this is an error givenName: abcdef -- role: +- + role: name: "r1" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-4-no-root-ns.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-4-no-root-ns.yaml deleted file mode 100644 index ef27ed80755..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-4-no-root-ns.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -objects: -- user: - name: "u1" - '@ns': "http://b/" -- user: - name: "u2" - '@ns': "http://c/" -- role: - name: "r1" - '@ns': "http://d/" diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-7-single.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-7-single.yaml deleted file mode 100644 index 17ba0c446d5..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-7-single.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -user: - - - name: u1 - givenName: g1 - familyName: f1 diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-8-multi-document.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-8-multi-document.yaml deleted file mode 100644 index bb21748bfa0..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-8-multi-document.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- user: - name: u1 -- user: - name: u2 ---- -- user: - name: u3 -- '@ns': "http://a/" - user: - name: u4 diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-9-list-single.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-9-list-single.yaml deleted file mode 100644 index a689949fb27..00000000000 --- a/infra/prism-impl/src/test/resources/common/yaml/objects-9-list-single.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- user: - name: u1 - givenName: g1 - familyName: f1 diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-json-yaml-1-incomplete-list.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-json-yaml-1-incomplete-list.yaml new file mode 100644 index 00000000000..cda3ff9fcf0 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/yaml/objects-json-yaml-1-incomplete-list.yaml @@ -0,0 +1,10 @@ +--- +- + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: u1 +- + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: u2 +- diff --git a/infra/prism-impl/src/test/resources/common/yaml/objects-yaml-1-multi-document.yaml b/infra/prism-impl/src/test/resources/common/yaml/objects-yaml-1-multi-document.yaml new file mode 100644 index 00000000000..71b4cbd2793 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/yaml/objects-yaml-1-multi-document.yaml @@ -0,0 +1,13 @@ +--- +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: u1 +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: + name: u2 +--- +- user: + name: u3 +- '@ns': "http://a/" + user: + name: u4 diff --git a/infra/prism-impl/src/test/resources/common/yaml/user-alice-metadata.yaml b/infra/prism-impl/src/test/resources/common/yaml/user-alice-metadata.yaml new file mode 100644 index 00000000000..71308c37738 --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/yaml/user-alice-metadata.yaml @@ -0,0 +1,26 @@ +"@ns": "http://midpoint.evolveum.com/xml/ns/test/foo-1.xsd" +user: + oid: "f6ba0f28-8875-4b41-9915-1277ed561d11" + version: 42 + "@metadata": + test: abc + name: + "@value": alice + "@metadata": + loa: high + additionalNames: + - + "@value": Jane + "@metadata": + loa: medium + - + "@value": Catherine + "@metadata": + loa: low + extension: + "@ns": "http://midpoint.evolveum.com/xml/ns/test/extension" + singleStringType: + "@ns": "http://midpoint.evolveum.com/xml/ns/test/foo-1.xsd" + "@value": "foobar" + "@metadata": + loa: low diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java index d211fb76236..9356bf4a98b 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/MidPointPrismContextFactory.java @@ -7,7 +7,6 @@ package com.evolveum.midpoint.schema; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collections; @@ -18,8 +17,8 @@ import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.metadata.MidpointValueMetadataFactory; -import com.evolveum.midpoint.schema.metadata.MidpointValueMetadataMockUpFactory; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueMetadataType; import com.evolveum.midpoint.xml.ns._public.model.model_3.ObjectFactory; import org.jetbrains.annotations.NotNull; import org.xml.sax.SAXException; @@ -44,7 +43,7 @@ public class MidPointPrismContextFactory implements PrismContextFactory { public static final MidPointPrismContextFactory FACTORY = new MidPointPrismContextFactory(TEST_EXTRA_SCHEMA_DIR); - private File extraSchemaDir; + private final File extraSchemaDir; public MidPointPrismContextFactory() { this.extraSchemaDir = null; @@ -55,7 +54,7 @@ public MidPointPrismContextFactory(File extraSchemaDir) { } @Override - public PrismContext createPrismContext() throws SchemaException, FileNotFoundException { + public PrismContext createPrismContext() throws SchemaException, IOException { SchemaRegistryImpl schemaRegistry = createSchemaRegistry(); PrismContextImpl context = PrismContextImpl.create(schemaRegistry); context.setDefinitionFactory(createDefinitionFactory()); @@ -66,12 +65,11 @@ public PrismContext createPrismContext() throws SchemaException, FileNotFoundExc context.setMonitor(new InternalMonitor()); } context.setParsingMigrator(new MidpointParsingMigrator()); - context.setValueMetadataMockUpFactory(new MidpointValueMetadataMockUpFactory(context)); context.setValueMetadataFactory(new MidpointValueMetadataFactory(context)); return context; } - public PrismContext createEmptyPrismContext() throws SchemaException, FileNotFoundException { + public PrismContext createEmptyPrismContext() throws SchemaException, IOException { SchemaRegistryImpl schemaRegistry = createSchemaRegistry(); PrismContextImpl context = PrismContextImpl.createEmptyContext(schemaRegistry); context.setDefinitionFactory(createDefinitionFactory()); @@ -92,16 +90,17 @@ public PrismContext createInitializedPrismContext() throws SchemaException, SAXE } @NotNull - private SchemaRegistryImpl createSchemaRegistry() throws SchemaException, FileNotFoundException { + private SchemaRegistryImpl createSchemaRegistry() throws SchemaException, IOException { SchemaRegistryImpl schemaRegistry = new SchemaRegistryImpl(); schemaRegistry.setDefaultNamespace(SchemaConstantsGenerated.NS_COMMON); schemaRegistry.setNamespacePrefixMapper(new GlobalDynamicNamespacePrefixMapper()); registerBuiltinSchemas(schemaRegistry); registerExtensionSchemas(schemaRegistry); + schemaRegistry.setValueMetadataTypeName(ValueMetadataType.COMPLEX_TYPE); return schemaRegistry; } - protected void registerExtensionSchemas(SchemaRegistryImpl schemaRegistry) throws SchemaException, FileNotFoundException { + protected void registerExtensionSchemas(SchemaRegistryImpl schemaRegistry) throws SchemaException, IOException { if (extraSchemaDir != null && extraSchemaDir.exists()) { schemaRegistry.registerPrismSchemasFromDirectory(extraSchemaDir); } @@ -132,7 +131,6 @@ private void registerBuiltinSchemas(SchemaRegistryImpl schemaRegistry) throws Sc com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectFactory.class.getPackage()); // declared by default schemaRegistry.registerPrismSchemaResource("xml/ns/public/common/extension-metadata-3.xsd", "meta-ext"); - schemaRegistry.registerPrismSchemaResource("xml/ns/public/common/extension-metadata-mock-3.xsd", "meta-mock-ext"); schemaRegistry.registerPrismSchemaResource("xml/ns/public/common/audit-3.xsd", "aud", com.evolveum.midpoint.xml.ns._public.common.audit_3.ObjectFactory.class.getPackage()); diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/metadata/MidpointValueMetadataMockUpFactory.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/metadata/MidpointValueMetadataMockUpFactory.java deleted file mode 100644 index 6ef4082b669..00000000000 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/metadata/MidpointValueMetadataMockUpFactory.java +++ /dev/null @@ -1,226 +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.midpoint.schema.metadata; - -import java.util.Optional; - -import org.jetbrains.annotations.NotNull; - -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.impl.metadata.ValueMetadataAdapter; -import com.evolveum.midpoint.prism.metadata.ValueMetadataMockUpFactory; -import com.evolveum.midpoint.prism.path.ItemName; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.util.CloneUtil; -import com.evolveum.midpoint.util.annotation.Experimental; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; - -/** - * TEMPORARY. WILL BE REMOVED AFTER PROTOTYPING IS OVER. - */ -@Experimental -public class MidpointValueMetadataMockUpFactory implements ValueMetadataMockUpFactory { - - private static final Trace LOGGER = TraceManager.getTrace(MidpointValueMetadataMockUpFactory.class); - - private static final String NS_MOCKUP = "http://midpoint.evolveum.com/xml/ns/public/common/extension-metadata-mockup-3"; - private static final ItemName ATTACHED_VALUE_METADATA_NAME = new ItemName(NS_MOCKUP, "attachedValueMetadata"); - - private static final ItemName ATTACHED_PATH = new ItemName(NS_MOCKUP, "path"); - private static final ItemName ATTACHED_VALUE = new ItemName(NS_MOCKUP, "value"); - private static final ItemName ATTACHED_METADATA = new ItemName(NS_MOCKUP, "metadata"); - private static final ItemName ATTACHED_SKIP_LEGACY_METADATA = new ItemName(NS_MOCKUP, "skipLegacyMetadata"); - - @NotNull private final PrismContext prismContext; - - public MidpointValueMetadataMockUpFactory(@NotNull PrismContext prismContext) { - this.prismContext = prismContext; - } - - @Override - public Optional createValueMetadata(@NotNull PrismValue value) throws SchemaException { - PrismObject object = getOwningObject(value); - if (object != null) { - ValueMetadataType metadata = createValueMetadata(object, value); - return metadata != null ? - Optional.of(ValueMetadataAdapter.holding(metadata.asPrismContainerValue())) : Optional.empty(); - } else { - LOGGER.warn("No owning object for {}, no metadata can be provided", value); - return Optional.empty(); - } - } - - private PrismObject getOwningObject(PrismValue value) { - Itemable parent = value.getParent(); - if (parent instanceof PrismObject) { - return ((PrismObject) parent); - } else if (parent instanceof Item) { - return getOwningObject(((Item) parent)); - } else { - return null; - } - } - - private PrismObject getOwningObject(Item item) { - PrismContainerValue parent = item.getParent(); - if (parent != null) { - return getOwningObject(parent); - } else { - return null; - } - } - - private ValueMetadataType createValueMetadata(PrismObject object, PrismValue value) throws SchemaException { - MetadataSources sources = findMetadataSources(object, value); - if (sources.attached == null && sources.legacy == null) { - return null; - } else { - ValueMetadataType aggregated; - if (sources.attached != null) { - aggregated = sources.attached.clone(); - } else { - aggregated = new ValueMetadataType(prismContext); - } - - if (sources.legacy != null) { - implantLegacy(aggregated, sources.legacy); - } - return aggregated; - } - } - - private void implantLegacy(ValueMetadataType aggregated, MetadataType legacy) throws SchemaException { - implant(aggregated, legacy, MetadataType.F_CREATE_TIMESTAMP, ValueMetadataType.F_STORAGE, StorageMetadataType.F_CREATE_TIMESTAMP); - implant(aggregated, legacy, MetadataType.F_CREATOR_REF, ValueMetadataType.F_STORAGE, StorageMetadataType.F_CREATOR_REF); - implant(aggregated, legacy, MetadataType.F_CREATE_CHANNEL, ValueMetadataType.F_STORAGE, StorageMetadataType.F_CREATE_CHANNEL); - implant(aggregated, legacy, MetadataType.F_CREATE_TASK_REF, ValueMetadataType.F_STORAGE, StorageMetadataType.F_CREATE_TASK_REF); - - implant(aggregated, legacy, MetadataType.F_MODIFY_TIMESTAMP, ValueMetadataType.F_STORAGE, StorageMetadataType.F_MODIFY_TIMESTAMP); - implant(aggregated, legacy, MetadataType.F_MODIFIER_REF, ValueMetadataType.F_STORAGE, StorageMetadataType.F_MODIFIER_REF); - implant(aggregated, legacy, MetadataType.F_MODIFY_CHANNEL, ValueMetadataType.F_STORAGE, StorageMetadataType.F_MODIFY_CHANNEL); - implant(aggregated, legacy, MetadataType.F_MODIFY_TASK_REF, ValueMetadataType.F_STORAGE, StorageMetadataType.F_MODIFY_TASK_REF); - - implant(aggregated, legacy, MetadataType.F_REQUEST_TIMESTAMP, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_REQUEST_TIMESTAMP); - implant(aggregated, legacy, MetadataType.F_REQUESTOR_REF, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_REQUESTOR_REF); - implant(aggregated, legacy, MetadataType.F_REQUESTOR_COMMENT, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_REQUESTOR_COMMENT); - implant(aggregated, legacy, MetadataType.F_CREATE_APPROVER_REF, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CREATE_APPROVER_REF); - implant(aggregated, legacy, MetadataType.F_CREATE_APPROVAL_COMMENT, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CREATE_APPROVAL_COMMENT); - implant(aggregated, legacy, MetadataType.F_CREATE_APPROVAL_TIMESTAMP, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CREATE_APPROVAL_TIMESTAMP); - implant(aggregated, legacy, MetadataType.F_MODIFY_APPROVER_REF, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_MODIFY_APPROVER_REF); - implant(aggregated, legacy, MetadataType.F_MODIFY_APPROVAL_COMMENT, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_MODIFY_APPROVAL_COMMENT); - implant(aggregated, legacy, MetadataType.F_MODIFY_APPROVAL_TIMESTAMP, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_MODIFY_APPROVAL_TIMESTAMP); - implant(aggregated, legacy, MetadataType.F_CERTIFICATION_FINISHED_TIMESTAMP, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CERTIFICATION_FINISHED_TIMESTAMP); - implant(aggregated, legacy, MetadataType.F_CERTIFICATION_OUTCOME, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CERTIFICATION_OUTCOME); - implant(aggregated, legacy, MetadataType.F_CERTIFIER_REF, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CERTIFIER_REF); - implant(aggregated, legacy, MetadataType.F_CERTIFIER_COMMENT, ValueMetadataType.F_PROCESS, ProcessMetadataType.F_CERTIFIER_COMMENT); - - implant(aggregated, legacy, MetadataType.F_LAST_PROVISIONING_TIMESTAMP, ValueMetadataType.F_PROVISIONING, ProvisioningMetadataType.F_LAST_PROVISIONING_TIMESTAMP); - - // origin mapping name is ignored - } - - private void implant(ValueMetadataType aggregated, MetadataType legacy, ItemName legacyName, ItemName... aggregatePathSegments) - throws SchemaException { - Item legacyItem = legacy.asPrismContainerValue().findItem(legacyName); - if (legacyItem != null) { - ItemDelta delta = prismContext.deltaFor(ValueMetadataType.class) - .item(aggregatePathSegments) - .replace(CloneUtil.cloneCollectionMembers(legacyItem.getValues())) - .asItemDelta(); - delta.applyTo(aggregated.asPrismContainerValue()); - } - } - - private static class MetadataSources { - private final ValueMetadataType attached; - private final MetadataType legacy; - - private MetadataSources(ValueMetadataType attached, MetadataType legacy) { - this.attached = attached; - this.legacy = legacy; - } - } - - private MetadataSources findMetadataSources(PrismObject object, PrismValue value) { - ItemPath path = value.getPath(); // will succeed as we know we can step up to the root object - //System.out.println("Deriving value metadata for " + value + " in " + object + " (path = " + path + ")"); - - ValueMetadataType attached; - PrismContainerValue attachedInfo = findAttachedMetadata(object, path, value); - boolean skipLegacy; - if (attachedInfo != null) { - PrismContainer attachedMetadataContainer = attachedInfo.findContainer(ATTACHED_METADATA); - attached = attachedMetadataContainer != null ? attachedMetadataContainer.getRealValue() : null; - skipLegacy = Boolean.TRUE.equals(attachedInfo.getPropertyRealValue(ATTACHED_SKIP_LEGACY_METADATA, Boolean.class)); - } else { - attached = null; - skipLegacy = false; - } - - MetadataType legacy; - if (!skipLegacy && value instanceof PrismContainerValue) { - PrismContainer legacyMetadataContainer = - ((PrismContainerValue) value).findContainer(ObjectType.F_METADATA); - legacy = legacyMetadataContainer != null ? legacyMetadataContainer.getRealValue() : null; - } else { - legacy = null; - } - - return new MetadataSources(attached, legacy); - } - - // returns AttachedValueMetadataType or null - private PrismContainerValue findAttachedMetadata(PrismObject object, ItemPath path, PrismValue value) { - PrismContainer allAttached = object.findExtensionItem(ATTACHED_VALUE_METADATA_NAME); - if (allAttached != null) { - for (PrismContainerValue attached : allAttached.getValues()) { - if (matches(attached, path, value)) { - return attached; - } - } - } - return null; - } - - private boolean matches(PrismContainerValue attached, ItemPath path, PrismValue value) { - ItemPath attachedPath = getAttachedPath(attached); - if (path.equivalent(attachedPath)) { - PrismProperty attachedValueProperty = attached.findProperty(ATTACHED_VALUE); - return attachedValueProperty == null || attachedValueProperty.isEmpty() - || valueMatches(attachedValueProperty.getValue(), value); - } else { - return false; - } - } - - // Temporary implementation, expecting attached is always a String - private boolean valueMatches(PrismPropertyValue attached, PrismValue real) { - String expected = (String) attached.getRealValue(); - return real.getRealValue() != null && real.getRealValue().toString().equals(expected); - } - - @NotNull - private ItemPath getAttachedPath(PrismContainerValue attached) { - PrismProperty pathProperty = attached.findProperty(ATTACHED_PATH); - if (pathProperty != null) { - ItemPathType attachedPathValue = pathProperty.getRealValue(); - if (attachedPathValue != null) { - return attachedPathValue.getItemPath(); - } else { - return ItemPath.EMPTY_PATH; - } - } else { - return ItemPath.EMPTY_PATH; - } - } -} diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java index 1be8d7fb8b7..22c582dc5b2 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java @@ -20,6 +20,7 @@ import com.evolveum.midpoint.prism.query.*; import com.evolveum.midpoint.prism.query.Visitor; import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit; +import com.evolveum.midpoint.prism.xnode.RootXNode; import com.evolveum.midpoint.schema.RelationRegistry; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.collections4.CollectionUtils; @@ -30,7 +31,6 @@ import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer; import com.evolveum.midpoint.prism.impl.polystring.AlphanumericPolyStringNormalizer; import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; -import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.prism.xml.ns._public.query_3.QueryType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; @@ -242,7 +242,8 @@ public static String dump(QueryType query, @NotNull PrismContext prismContext) t StringBuilder sb = new StringBuilder("Query("); sb.append(query.getDescription()).append("):\n"); if (query.getFilter() != null && query.getFilter().containsFilterClause()) { - sb.append(DOMUtil.serializeDOMToString(query.getFilter().getFilterClauseAsElement(prismContext))); + RootXNode clause = query.getFilter().getFilterClauseAsRootXNode(); + sb.append(prismContext.xmlSerializer().serialize(clause)); } else { sb.append("(no filter)"); } diff --git a/infra/schema/src/main/resources/META-INF/schemas-in-this-module.xml b/infra/schema/src/main/resources/META-INF/schemas-in-this-module.xml index 3e902f7e45b..b951ca830fa 100644 --- a/infra/schema/src/main/resources/META-INF/schemas-in-this-module.xml +++ b/infra/schema/src/main/resources/META-INF/schemas-in-this-module.xml @@ -48,7 +48,6 @@ Notes: - diff --git a/infra/schema/src/main/resources/xml/ns/public/common/extension-metadata-mock-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/extension-metadata-mock-3.xsd deleted file mode 100644 index 4e3e5e55d63..00000000000 --- a/infra/schema/src/main/resources/xml/ns/public/common/extension-metadata-mock-3.xsd +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - Object extension used to provide mockup of value metadata. - - The "attachedValueMetadata" extension item is used to derive mock-up metadata for all - relevant values in the object. - - FOR TESTING PURPOSES ONLY. WILL BE REMOVED SOON. - - - - - - - - - - - - - - - - - - - - - - - - - - Defines metadata attached to a specific value. - - - - - - - - - - Path to specific item value. For multivalued containers it should end with - container value id. For other values (properties, references) the value - should be specified by providing it to "value" item. - - - - - - - Specific value that should be provided with the metadata. - Due to prism serialization bug this works with string/polystring values only! - - - - - - - Attached metadata itself. - - - - - - - Whether to skip (not use) legacy metadata i.e. MetadataType. - - - - - - - - - diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java index 20351d533c1..14831a05540 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java @@ -111,22 +111,22 @@ public void testAccountFilter() throws Exception { ListXNode xequalsList = (ListXNode) xandmap.get(EqualFilter.ELEMENT_NAME); PrismAsserts.assertSize(xequalsList, 2); - Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); - System.out.println("Serialized filter (JAXB->DOM)"); - System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); - - DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); - DomAsserts.assertSubElements(filterClauseElement, 2); - - Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); - DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); - Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(firstValueElement, "abc"); - - Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); - DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); - Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(secondValueElement, "someName"); +// Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); +// System.out.println("Serialized filter (JAXB->DOM)"); +// System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); +// +// DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); +// DomAsserts.assertSubElements(filterClauseElement, 2); +// +// Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); +// DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); +// Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(firstValueElement, "abc"); +// +// Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); +// DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); +// Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(secondValueElement, "someName"); } @Test @@ -150,7 +150,7 @@ public void testAccountQueryAttributesAndResource() throws Exception { PrismAsserts.assertEqualsFilterValue((EqualFilter) second, "uid=jbond,ou=People,dc=example,dc=com"); QueryType convertedQueryType = toQueryType(query); - logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } @Test @@ -174,7 +174,7 @@ public void testAccountQueryAttributesAndResourceNoNs() throws Exception { //PrismAsserts.assertEqualsFilterValue((EqualFilter) second, "uid=jbond,ou=People,dc=example,dc=com"); QueryType convertedQueryType = toQueryType(query); - System.out.println(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// System.out.println(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } @Test @@ -208,7 +208,7 @@ public void testAccountQueryCompositeOr() throws Exception { assertRefFilterValue((RefFilter) forth, "d0db5be9-cb93-401f-b6c1-86ffffe4cd5e"); QueryType convertedQueryType = toQueryType(query); - logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } @Test @@ -385,7 +385,7 @@ public void testUserQuery() throws Exception { System.out.println("QUERY Pretty print: " + query.toString()); QueryType convertedQueryType = getQueryConverter().createQueryType(query); - logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } catch (Exception ex) { logger.error("Error while converting query: {}", ex.getMessage(), ex); throw ex; diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseObjects.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseObjects.java index 14199583258..f86d51c5ccd 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseObjects.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseObjects.java @@ -11,7 +11,6 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismParser; import com.evolveum.midpoint.prism.PrismSerializer; -import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectCollectionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; @@ -50,24 +49,14 @@ public void testRoundTrip() throws Exception { assertEquals("Wrong class of object 4", ObjectCollectionType.class, objects.get(3).asObjectable().getClass()); PrismSerializer serializer = prismContext.serializerFor(language); - String serializedAsObjects = serializer.serializeObjects(objects, SchemaConstants.C_OBJECTS); - System.out.println("Objects as re-serialized (c:objects):\n" + serializedAsObjects); - - System.out.println("Re-serialized to XML:\n" + prismContext.xmlSerializer().serializeObjects(objects, SchemaConstants.C_OBJECTS)); - System.out.println("Re-serialized to JSON:\n" + prismContext.jsonSerializer().serializeObjects(objects, SchemaConstants.C_OBJECTS)); - System.out.println("Re-serialized to YAML:\n" + prismContext.yamlSerializer().serializeObjects(objects, SchemaConstants.C_OBJECTS)); - - List> objectsReparsed = prismContext.parserFor(serializedAsObjects).parseObjects(); - assertEquals("Reparsed objects are different from original ones", objects, objectsReparsed); - - String serializedByDefault = serializer.serializeObjects(objects, null); + String serializedByDefault = serializer.serializeObjects(objects); System.out.println("Objects as re-serialized (default method):\n" + serializedByDefault); - System.out.println("Re-serialized to XML (default):\n" + prismContext.xmlSerializer().serializeObjects(objects, null)); - System.out.println("Re-serialized to JSON (default):\n" + prismContext.jsonSerializer().serializeObjects(objects, null)); - System.out.println("Re-serialized to YAML (default):\n" + prismContext.yamlSerializer().serializeObjects(objects, null)); + System.out.println("Re-serialized to XML (default):\n" + prismContext.xmlSerializer().serializeObjects(objects)); + System.out.println("Re-serialized to JSON (default):\n" + prismContext.jsonSerializer().serializeObjects(objects)); + System.out.println("Re-serialized to YAML (default):\n" + prismContext.yamlSerializer().serializeObjects(objects)); - List> objectsReparsedDefault = prismContext.parserFor(serializedAsObjects).parseObjects(); + List> objectsReparsedDefault = prismContext.parserFor(serializedByDefault).parseObjects(); assertEquals("Reparsed objects are different from original ones", objects, objectsReparsedDefault); } diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java index a1d8bd30bba..0529207774a 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java @@ -72,8 +72,8 @@ public void testNamespaces() throws Exception { final String common_3 = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; int count = StringUtils.countMatches(serialized, common_3); - // temporarily set from 2 to 4 (some XML weirdness in Xerces 2.12/Xalan 2.7.2); see MID-5661 - assertEquals("Wrong # of occurrences of '" + common_3 + "' in serialized form", 4, count); + // temporarily set from 2 to 3 (some XML weirdness in Xerces 2.12/Xalan 2.7.2); see MID-5661 + assertEquals("Wrong # of occurrences of '" + common_3 + "' in serialized form", 3, count); } // private void assertNamespaceDeclarations(String context, Element element, String... prefixes) { diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java index a8c12270fe6..e45780f2696 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java @@ -251,7 +251,7 @@ private void assertUserJaxb(UserType userType, boolean isObject) throws SchemaEx assertEquals("Wrong ref3 type (jaxb)", ShadowType.COMPLEX_TYPE, ref3.getType()); SearchFilterType ref3Filter = ref3.getFilter(); assertNotNull("No ref3 filter (jaxb,class)", ref3Filter); - assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); +// assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); } @Test diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java index 8e5589d3949..5e1904f6bd5 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java @@ -275,7 +275,7 @@ private void assertUserJaxb(UserType userType, boolean isObject) throws SchemaEx assertEquals("Wrong ref3 type (jaxb)", ShadowType.COMPLEX_TYPE, ref3.getType()); SearchFilterType ref3Filter = ref3.getFilter(); assertNotNull("No ref3 filter (jaxb,class)", ref3Filter); - assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); +// assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); } @Test diff --git a/infra/schema/src/test/resources/common/json/no-ns/objects-wrong.json b/infra/schema/src/test/resources/common/json/no-ns/objects-wrong.json index ec204e22a55..b7e2969c346 100644 --- a/infra/schema/src/test/resources/common/json/no-ns/objects-wrong.json +++ b/infra/schema/src/test/resources/common/json/no-ns/objects-wrong.json @@ -1,5 +1,4 @@ -{ - "objects" : [ { +[ { "user" : { "name" : "u1" } @@ -11,5 +10,5 @@ "role" : { "name" : "r1" } - } ] -} + } +] diff --git a/infra/schema/src/test/resources/common/json/no-ns/objects.json b/infra/schema/src/test/resources/common/json/no-ns/objects.json index c449eb6d974..b482f846d36 100644 --- a/infra/schema/src/test/resources/common/json/no-ns/objects.json +++ b/infra/schema/src/test/resources/common/json/no-ns/objects.json @@ -1,5 +1,4 @@ -{ - "objects" : [ { +[ { "user" : { "name" : "u1" } @@ -22,5 +21,5 @@ } } } - } ] -} + } +] diff --git a/infra/schema/src/test/resources/common/json/ns/objects-wrong.json b/infra/schema/src/test/resources/common/json/ns/objects-wrong.json index d4d790f2c5e..f108c3a8993 100644 --- a/infra/schema/src/test/resources/common/json/ns/objects-wrong.json +++ b/infra/schema/src/test/resources/common/json/ns/objects-wrong.json @@ -1,16 +1,20 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { +[ + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "user" : { "name" : "u1" } - }, { + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "user" : { "name-error" : "u2" } - }, { + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "role" : { "name" : "r1" } - } ] -} + } +] diff --git a/infra/schema/src/test/resources/common/json/ns/objects.json b/infra/schema/src/test/resources/common/json/ns/objects.json index 4aedf28fcfe..43264b08925 100644 --- a/infra/schema/src/test/resources/common/json/ns/objects.json +++ b/infra/schema/src/test/resources/common/json/ns/objects.json @@ -1,18 +1,24 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { +[ + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "user" : { "name" : "u1" } - }, { + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "user" : { "name" : "u2" } - }, { + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "role" : { "name" : "r1" } - }, { + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", "objectCollection" : { "name" : "oc", "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#UserType", @@ -24,5 +30,5 @@ } } } - } ] -} + } +] diff --git a/infra/schema/src/test/resources/common/yaml/no-ns/objects-wrong.yaml b/infra/schema/src/test/resources/common/yaml/no-ns/objects-wrong.yaml index 18736379ba1..92c85b8e513 100644 --- a/infra/schema/src/test/resources/common/yaml/no-ns/objects-wrong.yaml +++ b/infra/schema/src/test/resources/common/yaml/no-ns/objects-wrong.yaml @@ -1,5 +1,4 @@ --- -objects: - user: name: "u1" - user: diff --git a/infra/schema/src/test/resources/common/yaml/no-ns/objects.yaml b/infra/schema/src/test/resources/common/yaml/no-ns/objects.yaml index b76f80ec4aa..c907c504c93 100644 --- a/infra/schema/src/test/resources/common/yaml/no-ns/objects.yaml +++ b/infra/schema/src/test/resources/common/yaml/no-ns/objects.yaml @@ -1,12 +1,15 @@ --- -objects: -- user: +- + user: name: "u1" -- user: +- + user: name: "u2" -- role: +- + role: name: "r1" -- objectCollection: +- + objectCollection: name: "oc" type: "UserType" filter: diff --git a/infra/schema/src/test/resources/common/yaml/ns/objects-wrong.yaml b/infra/schema/src/test/resources/common/yaml/ns/objects-wrong.yaml index 7d5c967a656..ccd22057823 100644 --- a/infra/schema/src/test/resources/common/yaml/ns/objects-wrong.yaml +++ b/infra/schema/src/test/resources/common/yaml/ns/objects-wrong.yaml @@ -1,9 +1,10 @@ --- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- user: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: name: "u1" -- user: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: name-error: "u2" -- role: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + role: name: "r1" diff --git a/infra/schema/src/test/resources/common/yaml/ns/objects.yaml b/infra/schema/src/test/resources/common/yaml/ns/objects.yaml index 32fff30ae90..ca6960ddbd4 100644 --- a/infra/schema/src/test/resources/common/yaml/ns/objects.yaml +++ b/infra/schema/src/test/resources/common/yaml/ns/objects.yaml @@ -1,13 +1,15 @@ --- -'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- user: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: name: "u1" -- user: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + user: name: "u2" -- role: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + role: name: "r1" -- objectCollection: +- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + objectCollection: name: "oc" type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#UserType" filter: diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java index 95fe3c1d770..847abcb1bb2 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java @@ -764,11 +764,10 @@ public static Map getNamespaceDeclarations(Element element) { } public static void setNamespaceDeclarations(Element element, Map rootNamespaceDeclarations) { - if (rootNamespaceDeclarations == null) { - return; - } - for (Entry entry : rootNamespaceDeclarations.entrySet()) { - setNamespaceDeclaration(element, entry.getKey(), entry.getValue()); + if (rootNamespaceDeclarations != null) { + for (Entry entry : rootNamespaceDeclarations.entrySet()) { + setNamespaceDeclaration(element, entry.getKey(), entry.getValue()); + } } } @@ -953,6 +952,15 @@ public static Element getChildElement(Element element, QName qname) { return null; } + public static Element getMatchingChildElement(Element element, QName qname) { + for (Element subelement: listChildElements(element)) { + if (QNameUtil.match(qname, getQName(subelement))) { + return subelement; + } + } + return null; + } + public static Element getNamedElement(List elements, QName qname) { for (Element element : elements) { if (qname.equals(getQName(element))) { diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingParser.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingParser.java index 7de2239f28e..ee61f470f10 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingParser.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/mapping/MappingParser.java @@ -57,15 +57,6 @@ void parseSourcesAndTarget(OperationResult result) throws SchemaException, Commu parseSources(result); parseTarget(); assertOutputDefinition(); - - fixMockUpSourceValueMetadata(); - } - - /** - * Temporary. "Fixes" mock-up source value metadata i.e. transforms them into materialized form. - */ - private void fixMockUpSourceValueMetadata() { - m.sources.forEach(ItemDeltaItem::fixMockUpValueMetadata); } private void assertOutputDefinition() { diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java index 7cb1519d574..003c711a2d9 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestValueMetadata.java @@ -72,7 +72,9 @@ public void test010KeepingLiveMetadata() throws SchemaException { PrismPropertyValue nameValue = mark.asPrismObject() .findProperty(UserType.F_NAME) .getValue(PolyString.class); - nameValue.createLiveMetadata(); + + // Creates empty value metadata + nameValue.getValueMetadata(); when(); Optional metadata = nameValue.valueMetadata(); diff --git a/model/model-intest/src/test/resources/importer/import-bad.json b/model/model-intest/src/test/resources/importer/import-bad.json index 5cf2c54cf4f..5349cd68e0f 100644 --- a/model/model-intest/src/test/resources/importer/import-bad.json +++ b/model/model-intest/src/test/resources/importer/import-bad.json @@ -1,44 +1,50 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "user" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111111", - "name" : "jack", - "extension" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/samples/piracy", - "ship" : "Black Pearl" +[ + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111111", + "name": "jack", + "extension": { + "@ns": "http://midpoint.evolveum.com/xml/ns/samples/piracy", + "ship": "Black Pearl" }, - "fullName" : "Cpt. Jack Sparrow", - "givenName" : "Jack", - "familyName" : "Sparrow", - "honorificPrefix" : "Cpt." + "fullName": "Cpt. Jack Sparrow", + "givenName": "Jack", + "familyName": "Sparrow", + "honorificPrefix": "Cpt." } - }, { - "user" : { - "fullName" : "Will Turner", - "givenName" : "William", - "familyName" : "Turner" + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "fullName": "Will Turner", + "givenName": "William", + "familyName": "Turner" } - }, { - "user" : { - "name" : "guybrush", - "fullName" : "Guybrush Threepwood", - "givenName" : "Guybrush", - "familyName" : "Threepwood" + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "name": "guybrush", + "fullName": "Guybrush Threepwood", + "givenName": "Guybrush", + "familyName": "Threepwood" } - }, { - "account" : { - "name" : "Wrong Ref Account", - "resourceRef" : { - "oid" : "d0e5707e-8157-3333-4444-555511111111", - "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ResourceType" + }, + { + "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "account": { + "name": "Wrong Ref Account", + "resourceRef": { + "oid": "d0e5707e-8157-3333-4444-555511111111", + "type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ResourceType" }, - "objectClass" : "AccountObjectClass", - "attributes" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/samples/piracy", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ShadowAttributesType", - "bar" : "foobar" + "objectClass": "AccountObjectClass", + "attributes": { + "@ns": "http://midpoint.evolveum.com/xml/ns/samples/piracy", + "@type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ShadowAttributesType", + "bar": "foobar" } } - } ] -} + } +] diff --git a/model/model-intest/src/test/resources/importer/import-ref.json b/model/model-intest/src/test/resources/importer/import-ref.json index 51a77c9d5fe..a299db06b82 100644 --- a/model/model-intest/src/test/resources/importer/import-ref.json +++ b/model/model-intest/src/test/resources/importer/import-ref.json @@ -1,35 +1,37 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "resource" : { - "name" : "Sample Resource", - "connectorRef" : { - "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", - "filter" : { - "@ns" : "http://prism.evolveum.com/xml/ns/public/query-3", - "equal" : { - "path" : "connectorType", - "value" : "com.evolveum.polygon.connector.ldap.LdapConnector" +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "resource": { + "name": "Sample Resource", + "connectorRef": { + "type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", + "filter": { + "@ns": "http://prism.evolveum.com/xml/ns/public/query-3", + "equal": { + "path": "connectorType", + "value": "com.evolveum.polygon.connector.ldap.LdapConnector" } } }, - "connectorConfiguration" : { - "@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType" + "connectorConfiguration": { + "@type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType" }, - "namespace" : "http://foo.bar.com/", - "schema" : { } + "namespace": "http://foo.bar.com/", + "schema": {} } - }, { - "user" : { - "name" : "jack2", - "extension" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/samples/piracy", - "ship" : "Black Pearl" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "name": "jack2", + "extension": { + "@ns": "http://midpoint.evolveum.com/xml/ns/samples/piracy", + "ship": "Black Pearl" }, - "fullName" : "Cpt. Jack Sparrow", - "givenName" : "Jack", - "familyName" : "Sparrow", - "honorificPrefix" : "Cpt." + "fullName": "Cpt. Jack Sparrow", + "givenName": "Jack", + "familyName": "Sparrow", + "honorificPrefix": "Cpt." } - } ] -} + } +] diff --git a/model/model-intest/src/test/resources/importer/import-task.json b/model/model-intest/src/test/resources/importer/import-task.json index 17e9f801fae..1d4a15235ae 100644 --- a/model/model-intest/src/test/resources/importer/import-task.json +++ b/model/model-intest/src/test/resources/importer/import-task.json @@ -1,24 +1,24 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "task" : { - "oid" : "00000000-0000-0000-0000-123450000001", - "name" : "Task1: basic single-run task (takes 180x1 sec)", - "extension" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ExtensionType", - "delay" : 1000, - "steps" : 10 +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "task": { + "oid": "00000000-0000-0000-0000-123450000001", + "name": "Task1: basic single-run task (takes 180x1 sec)", + "extension": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3", + "@type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ExtensionType", + "delay": 1000, + "steps": 10 }, - "taskIdentifier" : "10000000-0000-0000-0000-123450000001", - "ownerRef" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111111", - "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#UserType" + "taskIdentifier": "10000000-0000-0000-0000-123450000001", + "ownerRef": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111111", + "type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#UserType" }, - "executionStatus" : "runnable", - "handlerUri" : "http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3", - "recurrence" : "single", - "binding" : "tight" + "executionStatus": "runnable", + "handlerUri": "http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3", + "recurrence": "single", + "binding": "tight" } - } ] -} + } +] diff --git a/model/model-intest/src/test/resources/importer/import-task.yaml b/model/model-intest/src/test/resources/importer/import-task.yaml index 0311cc7d3ec..93891cf17ee 100644 --- a/model/model-intest/src/test/resources/importer/import-task.yaml +++ b/model/model-intest/src/test/resources/importer/import-task.yaml @@ -1,7 +1,6 @@ --- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- task: +task: oid: "00000000-0000-0000-0000-123450000001" name: "Task1: basic single-run task (takes 180x1 sec)" extension: ! diff --git a/model/model-intest/src/test/resources/importer/import-users-overwrite.json b/model/model-intest/src/test/resources/importer/import-users-overwrite.json index eea9cd2c673..a9985128fc8 100644 --- a/model/model-intest/src/test/resources/importer/import-users-overwrite.json +++ b/model/model-intest/src/test/resources/importer/import-users-overwrite.json @@ -1,45 +1,53 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "user" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111112", - "name" : "will", - "fullName" : "Will Turner", - "givenName" : "William", - "familyName" : "Turner", - "subtype" : "legendary" +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111112", + "name": "will", + "fullName": "Will Turner", + "givenName": "William", + "familyName": "Turner", + "subtype": "legendary" } - }, { - "user" : { - "name" : "guybrush", - "fullName" : "Guybrush Threepwood", - "givenName" : "Guybrush", - "familyName" : "Threepwood", - "locality" : "Deep in the Caribbean" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "name": "guybrush", + "fullName": "Guybrush Threepwood", + "givenName": "Guybrush", + "familyName": "Threepwood", + "locality": "Deep in the Caribbean" } - }, { - "user" : { - "name" : "ht", - "fullName" : "Herman Toothrot", - "givenName" : "Herman", - "familyName" : "Toothrot" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "name": "ht", + "fullName": "Herman Toothrot", + "givenName": "Herman", + "familyName": "Toothrot" } - }, { - "user" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111113", - "name" : "elizabethT", - "fullName" : "Elizabeth Turner", - "givenName" : "Elizabeth", - "familyName" : "Turner" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111113", + "name": "elizabethT", + "fullName": "Elizabeth Turner", + "givenName": "Elizabeth", + "familyName": "Turner" } - }, { - "user" : { - "oid" : "12345", - "name" : "james", - "fullName" : "James Norrington", - "givenName" : "James", - "familyName" : "Norrington", - "subtype" : "admiral" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "12345", + "name": "james", + "fullName": "James Norrington", + "givenName": "James", + "familyName": "Norrington", + "subtype": "admiral" } - } ] -} + } +] diff --git a/model/model-intest/src/test/resources/importer/import-users-overwrite.yaml b/model/model-intest/src/test/resources/importer/import-users-overwrite.yaml index 877cc2d962d..1ee07a0633e 100644 --- a/model/model-intest/src/test/resources/importer/import-users-overwrite.yaml +++ b/model/model-intest/src/test/resources/importer/import-users-overwrite.yaml @@ -1,34 +1,41 @@ --- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- user: - oid: "c0c010c0-d34d-b33f-f00d-111111111112" - name: "will" - fullName: "Will Turner" - givenName: "William" - familyName: "Turner" - subtype: "legendary" -- user: - name: "guybrush" - fullName: "Guybrush Threepwood" - givenName: "Guybrush" - familyName: "Threepwood" - locality: "Deep in the Caribbean" -- user: - name: "ht" - fullName: "Herman Toothrot" - givenName: "Herman" - familyName: "Toothrot" -- user: - oid: "c0c010c0-d34d-b33f-f00d-111111111113" - name: "elizabethT" - fullName: "Elizabeth Turner" - givenName: "Elizabeth" - familyName: "Turner" -- user: - oid: "12345" - name: "james" - fullName: "James Norrington" - givenName: "James" - familyName: "Norrington" - subtype: "admiral" +user: + oid: "c0c010c0-d34d-b33f-f00d-111111111112" + name: "will" + fullName: "Will Turner" + givenName: "William" + familyName: "Turner" + subtype: "legendary" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + name: "guybrush" + fullName: "Guybrush Threepwood" + givenName: "Guybrush" + familyName: "Threepwood" + locality: "Deep in the Caribbean" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + name: "ht" + fullName: "Herman Toothrot" + givenName: "Herman" + familyName: "Toothrot" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + oid: "c0c010c0-d34d-b33f-f00d-111111111113" + name: "elizabethT" + fullName: "Elizabeth Turner" + givenName: "Elizabeth" + familyName: "Turner" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + oid: "12345" + name: "james" + fullName: "James Norrington" + givenName: "James" + familyName: "Norrington" + subtype: "admiral" diff --git a/model/model-intest/src/test/resources/importer/import-users.json b/model/model-intest/src/test/resources/importer/import-users.json index 3ae60b9b1d2..fd51a20df92 100644 --- a/model/model-intest/src/test/resources/importer/import-users.json +++ b/model/model-intest/src/test/resources/importer/import-users.json @@ -1,56 +1,64 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "user" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111111", - "name" : "jack", - "extension" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/samples/piracy", - "ship" : "Black Pearl" +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111111", + "name": "jack", + "extension": { + "@ns": "http://midpoint.evolveum.com/xml/ns/samples/piracy", + "ship": "Black Pearl" }, - "fullName" : "Cpt. Jack Sparrow", - "givenName" : "Jack", - "familyName" : "Sparrow", - "honorificPrefix" : "Cpt.", - "costCenter" : "", - "credentials" : { - "password" : { - "value" : { - "@ns" : "http://prism.evolveum.com/xml/ns/public/types-3", - "clearValue" : "dead men tell no tales" + "fullName": "Cpt. Jack Sparrow", + "givenName": "Jack", + "familyName": "Sparrow", + "honorificPrefix": "Cpt.", + "costCenter": "", + "credentials": { + "password": { + "value": { + "@ns": "http://prism.evolveum.com/xml/ns/public/types-3", + "clearValue": "dead men tell no tales" } } } } - }, { - "user" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111112", - "name" : "will", - "fullName" : "Will Turner", - "givenName" : "William", - "familyName" : "Turner" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111112", + "name": "will", + "fullName": "Will Turner", + "givenName": "William", + "familyName": "Turner" } - }, { - "user" : { - "name" : "guybrush", - "fullName" : "Guybrush Threepwood", - "givenName" : "Guybrush", - "familyName" : "Threepwood" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "name": "guybrush", + "fullName": "Guybrush Threepwood", + "givenName": "Guybrush", + "familyName": "Threepwood" } - }, { - "user" : { - "oid" : "c0c010c0-d34d-b33f-f00d-111111111113", - "name" : "elizabethS", - "fullName" : "Elizabeth Swann", - "givenName" : "Elizabeth", - "familyName" : "Swann" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "oid": "c0c010c0-d34d-b33f-f00d-111111111113", + "name": "elizabethS", + "fullName": "Elizabeth Swann", + "givenName": "Elizabeth", + "familyName": "Swann" } - }, { - "user" : { - "name" : "james", - "fullName" : "James Norrington", - "givenName" : "James", - "familyName" : "Norrington" + }, + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "user": { + "name": "james", + "fullName": "James Norrington", + "givenName": "James", + "familyName": "Norrington" } - } ] -} + } +] diff --git a/model/model-intest/src/test/resources/importer/import-users.yaml b/model/model-intest/src/test/resources/importer/import-users.yaml index 68588b28578..90ed21cfc4b 100644 --- a/model/model-intest/src/test/resources/importer/import-users.yaml +++ b/model/model-intest/src/test/resources/importer/import-users.yaml @@ -1,41 +1,48 @@ --- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- user: - oid: "c0c010c0-d34d-b33f-f00d-111111111111" - name: "jack" - extension: - '@ns': "http://midpoint.evolveum.com/xml/ns/samples/piracy" - ship: "Black Pearl" - fullName: "Cpt. Jack Sparrow" - givenName: "Jack" - familyName: "Sparrow" - honorificPrefix: "Cpt." - costCenter: "" - credentials: - password: - value: - '@ns': "http://prism.evolveum.com/xml/ns/public/types-3" - clearValue: "dead men tell no tales" -- user: - oid: "c0c010c0-d34d-b33f-f00d-111111111112" - name: "will" - fullName: "Will Turner" - givenName: "William" - familyName: "Turner" -- user: - name: "guybrush" - fullName: "Guybrush Threepwood" - givenName: "Guybrush" - familyName: "Threepwood" -- user: - oid: "c0c010c0-d34d-b33f-f00d-111111111113" - name: "elizabethS" - fullName: "Elizabeth Swann" - givenName: "Elizabeth" - familyName: "Swann" -- user: - name: "james" - fullName: "James Norrington" - givenName: "James" - familyName: "Norrington" +user: + oid: "c0c010c0-d34d-b33f-f00d-111111111111" + name: "jack" + extension: + '@ns': "http://midpoint.evolveum.com/xml/ns/samples/piracy" + ship: "Black Pearl" + fullName: "Cpt. Jack Sparrow" + givenName: "Jack" + familyName: "Sparrow" + honorificPrefix: "Cpt." + costCenter: "" + credentials: + password: + value: + '@ns': "http://prism.evolveum.com/xml/ns/public/types-3" + clearValue: "dead men tell no tales" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + oid: "c0c010c0-d34d-b33f-f00d-111111111112" + name: "will" + fullName: "Will Turner" + givenName: "William" + familyName: "Turner" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + name: "guybrush" + fullName: "Guybrush Threepwood" + givenName: "Guybrush" + familyName: "Threepwood" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + oid: "c0c010c0-d34d-b33f-f00d-111111111113" + name: "elizabethS" + fullName: "Elizabeth Swann" + givenName: "Elizabeth" + familyName: "Swann" +--- +'@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" +user: + name: "james" + fullName: "James Norrington" + givenName: "James" + familyName: "Norrington" diff --git a/model/model-intest/src/test/resources/importer/resource-derby.json b/model/model-intest/src/test/resources/importer/resource-derby.json index d4e9ddf3f94..d1f1404de1f 100644 --- a/model/model-intest/src/test/resources/importer/resource-derby.json +++ b/model/model-intest/src/test/resources/importer/resource-derby.json @@ -1,44 +1,44 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "resource" : { - "oid" : "ef2bc95b-76e0-59e2-86d6-9119011311ab", - "name" : "Embedded Test Derby: Import test", - "connectorRef" : { - "oid" : "7d3ebd6f-6113-4833-8a6a-596b73a5e434", - "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", - "filter" : { - "@ns" : "http://prism.evolveum.com/xml/ns/public/query-3", - "equal" : { - "path" : "connectorType", - "value" : "com.evolveum.icf.dummy.connector.DummyConnector" - } - } - }, - "connectorConfiguration" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType", - "configurationProperties" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/org.forgerock.openicf.connectors.db.databasetable/org.identityconnectors.databasetable.DatabaseTableConnector", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3#ConfigurationPropertiesType", - "jdbcDriver" : "org.apache.derby.jdbc.ClientDriver", - "jdbcUrlTemplate" : "jdbc:derby://%h:%p/%d", - "keyColumn" : "login", - "passwordColumn" : "password", - "password" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "clearValue" : "secret" - }, - "table" : "users", - "user" : "midpoint", - "database" : "target/derbyMidPointTest", - "host" : "localhost", - "port" : "11527" - } - }, - "namespace" : "http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-9119011311ab", - "schema" : { }, - "schemaHandling" : { } +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "resource": { + "oid": "ef2bc95b-76e0-59e2-86d6-9119011311ab", + "name": "Embedded Test Derby: Import test", + "connectorRef": { + "oid": "7d3ebd6f-6113-4833-8a6a-596b73a5e434", + "type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", + "filter": { + "@ns": "http://prism.evolveum.com/xml/ns/public/query-3", + "equal": { + "path": "connectorType", + "value": "com.evolveum.icf.dummy.connector.DummyConnector" + } } - } ] -} + }, + "connectorConfiguration": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", + "@type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType", + "configurationProperties": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/org.forgerock.openicf.connectors.db.databasetable/org.identityconnectors.databasetable.DatabaseTableConnector", + "@type": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3#ConfigurationPropertiesType", + "jdbcDriver": "org.apache.derby.jdbc.ClientDriver", + "jdbcUrlTemplate": "jdbc:derby://%h:%p/%d", + "keyColumn": "login", + "passwordColumn": "password", + "password": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "clearValue": "secret" + }, + "table": "users", + "user": "midpoint", + "database": "target/derbyMidPointTest", + "host": "localhost", + "port": "11527" + } + }, + "namespace": "http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-9119011311ab", + "schema": {}, + "schemaHandling": {} + } + } +] diff --git a/model/model-intest/src/test/resources/importer/resource-derby.yaml b/model/model-intest/src/test/resources/importer/resource-derby.yaml index 440ee8f5154..6e21abb231d 100644 --- a/model/model-intest/src/test/resources/importer/resource-derby.yaml +++ b/model/model-intest/src/test/resources/importer/resource-derby.yaml @@ -1,33 +1,32 @@ --- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- resource: - oid: "ef2bc95b-76e0-59e2-86d6-9119011311ab" - name: "Embedded Test Derby: Import test" - connectorRef: - oid: "7d3ebd6f-6113-4833-8a6a-596b73a5e434" - type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType" - filter: - '@ns': "http://prism.evolveum.com/xml/ns/public/query-3" - equal: - path: "connectorType" - value: "com.evolveum.icf.dummy.connector.DummyConnector" - connectorConfiguration: ! - '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3" - configurationProperties: ! - '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/org.forgerock.openicf.connectors.db.databasetable/org.identityconnectors.databasetable.DatabaseTableConnector" - jdbcDriver: "org.apache.derby.jdbc.ClientDriver" - jdbcUrlTemplate: "jdbc:derby://%h:%p/%d" - keyColumn: "login" - passwordColumn: "password" - password: - '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" - clearValue: "secret" - table: "users" - user: "midpoint" - database: "target/derbyMidPointTest" - host: "localhost" - port: "11527" - namespace: "http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-9119011311ab" - schema: {} - schemaHandling: {} +resource: + oid: "ef2bc95b-76e0-59e2-86d6-9119011311ab" + name: "Embedded Test Derby: Import test" + connectorRef: + oid: "7d3ebd6f-6113-4833-8a6a-596b73a5e434" + type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType" + filter: + '@ns': "http://prism.evolveum.com/xml/ns/public/query-3" + equal: + path: "connectorType" + value: "com.evolveum.icf.dummy.connector.DummyConnector" + connectorConfiguration: ! + '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3" + configurationProperties: ! + '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/org.forgerock.openicf.connectors.db.databasetable/org.identityconnectors.databasetable.DatabaseTableConnector" + jdbcDriver: "org.apache.derby.jdbc.ClientDriver" + jdbcUrlTemplate: "jdbc:derby://%h:%p/%d" + keyColumn: "login" + passwordColumn: "password" + password: + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + clearValue: "secret" + table: "users" + user: "midpoint" + database: "target/derbyMidPointTest" + host: "localhost" + port: "11527" + namespace: "http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-9119011311ab" + schema: {} + schemaHandling: {} diff --git a/model/model-intest/src/test/resources/importer/resource-dummy-changed.json b/model/model-intest/src/test/resources/importer/resource-dummy-changed.json index 7895243dc84..b63bcaf35c1 100644 --- a/model/model-intest/src/test/resources/importer/resource-dummy-changed.json +++ b/model/model-intest/src/test/resources/importer/resource-dummy-changed.json @@ -1,39 +1,42 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "resource" : { - "oid" : "10000000-0000-0000-0000-000000000004", - "name" : "Dummy Resource", - "connectorRef" : { - "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", - "filter" : { - "@ns" : "http://prism.evolveum.com/xml/ns/public/query-3", - "and" : { - "equal" : [ { - "path" : "connectorType", - "value" : "com.evolveum.icf.dummy.connector.DummyConnector" - }, { - "path" : "connectorVersion", - "value" : "2.0" - } ] - } - } - }, - "connectorConfiguration" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType", - "configurationProperties" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3#ConfigurationPropertiesType", - "instanceId" : "", - "requireExplicitEnable" : "true", - "uselessGuardedString" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "clearValue" : "whatever" - } - } - }, - "namespace" : "http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004" +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "resource": { + "oid": "10000000-0000-0000-0000-000000000004", + "name": "Dummy Resource", + "connectorRef": { + "type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", + "filter": { + "@ns": "http://prism.evolveum.com/xml/ns/public/query-3", + "and": { + "equal": [ + { + "path": "connectorType", + "value": "com.evolveum.icf.dummy.connector.DummyConnector" + }, + { + "path": "connectorVersion", + "value": "2.0" + } + ] + } } - } ] -} + }, + "connectorConfiguration": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", + "@type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType", + "configurationProperties": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector", + "@type": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3#ConfigurationPropertiesType", + "instanceId": "", + "requireExplicitEnable": "true", + "uselessGuardedString": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "clearValue": "whatever" + } + } + }, + "namespace": "http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004" + } + } +] diff --git a/model/model-intest/src/test/resources/importer/resource-dummy-changed.yaml b/model/model-intest/src/test/resources/importer/resource-dummy-changed.yaml index c777459c6e3..eaf5c0e4460 100644 --- a/model/model-intest/src/test/resources/importer/resource-dummy-changed.yaml +++ b/model/model-intest/src/test/resources/importer/resource-dummy-changed.yaml @@ -1,26 +1,25 @@ --- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- resource: - oid: "10000000-0000-0000-0000-000000000004" - name: "Dummy Resource" - connectorRef: - type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType" - filter: - '@ns': "http://prism.evolveum.com/xml/ns/public/query-3" - and: - equal: +resource: + oid: "10000000-0000-0000-0000-000000000004" + name: "Dummy Resource" + connectorRef: + type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType" + filter: + '@ns': "http://prism.evolveum.com/xml/ns/public/query-3" + and: + equal: - path: "connectorType" value: "com.evolveum.icf.dummy.connector.DummyConnector" - path: "connectorVersion" value: "2.0" - connectorConfiguration: ! - '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3" - configurationProperties: ! - '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector" - instanceId: "" - requireExplicitEnable: "true" - uselessGuardedString: - '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" - clearValue: "whatever" - namespace: "http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004" + connectorConfiguration: ! + '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3" + configurationProperties: ! + '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector" + instanceId: "" + requireExplicitEnable: "true" + uselessGuardedString: + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + clearValue: "whatever" + namespace: "http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004" diff --git a/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.json b/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.json index d75cdf5c17b..2ab1a037f6d 100644 --- a/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.json +++ b/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.json @@ -1,39 +1,42 @@ -{ - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "objects" : [ { - "resource" : { - "oid" : "78fc521e-69f0-11e6-9ec5-130eb0c6fb6d", - "name" : "Dummy Resource (runtime)", - "connectorRef" : { - "type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", - "filter" : { - "@ns" : "http://prism.evolveum.com/xml/ns/public/query-3", - "and" : { - "equal" : [ { - "path" : "connectorType", - "value" : "com.evolveum.icf.dummy.connector.DummyConnector" - }, { - "path" : "connectorVersion", - "value" : "2.0" - } ] +[ + { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "resource": { + "oid": "78fc521e-69f0-11e6-9ec5-130eb0c6fb6d", + "name": "Dummy Resource (runtime)", + "connectorRef": { + "type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType", + "filter": { + "@ns": "http://prism.evolveum.com/xml/ns/public/query-3", + "and": { + "equal": [ + { + "path": "connectorType", + "value": "com.evolveum.icf.dummy.connector.DummyConnector" + }, + { + "path": "connectorVersion", + "value": "2.0" + } + ] } }, - "resolutionTime" : "run" + "resolutionTime": "run" }, - "connectorConfiguration" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType", - "configurationProperties" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector", - "@type" : "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3#ConfigurationPropertiesType", - "instanceId" : "runtime", - "requireExplicitEnable" : "true", - "uselessGuardedString" : { - "@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3", - "clearValue" : "whatever" + "connectorConfiguration": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", + "@type": "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorConfigurationType", + "configurationProperties": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector", + "@type": "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3#ConfigurationPropertiesType", + "instanceId": "runtime", + "requireExplicitEnable": "true", + "uselessGuardedString": { + "@ns": "http://midpoint.evolveum.com/xml/ns/public/common/common-3", + "clearValue": "whatever" } } } } - } ] -} + } +] diff --git a/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.yaml b/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.yaml index f085dd99255..bf3f5359b63 100644 --- a/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.yaml +++ b/model/model-intest/src/test/resources/importer/resource-dummy-runtime-resolution.yaml @@ -1,26 +1,25 @@ --- '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" -objects: -- resource: - oid: "78fc521e-69f0-11e6-9ec5-130eb0c6fb6d" - name: "Dummy Resource (runtime)" - connectorRef: - type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType" - filter: - '@ns': "http://prism.evolveum.com/xml/ns/public/query-3" - and: - equal: +resource: + oid: "78fc521e-69f0-11e6-9ec5-130eb0c6fb6d" + name: "Dummy Resource (runtime)" + connectorRef: + type: "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ConnectorType" + filter: + '@ns': "http://prism.evolveum.com/xml/ns/public/query-3" + and: + equal: - path: "connectorType" value: "com.evolveum.icf.dummy.connector.DummyConnector" - path: "connectorVersion" value: "2.0" - resolutionTime: "run" - connectorConfiguration: ! - '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3" - configurationProperties: ! - '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector" - instanceId: "runtime" - requireExplicitEnable: "true" - uselessGuardedString: - '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" - clearValue: "whatever" + resolutionTime: "run" + connectorConfiguration: ! + '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3" + configurationProperties: ! + '@ns': "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.icf.dummy/com.evolveum.icf.dummy.connector.DummyConnector" + instanceId: "runtime" + requireExplicitEnable: "true" + uselessGuardedString: + '@ns': "http://midpoint.evolveum.com/xml/ns/public/common/common-3" + clearValue: "whatever" diff --git a/model/model-intest/src/test/resources/metadata/user-alice.xml b/model/model-intest/src/test/resources/metadata/user-alice.xml index 87eb5567fa3..ac68684ff75 100644 --- a/model/model-intest/src/test/resources/metadata/user-alice.xml +++ b/model/model-intest/src/test/resources/metadata/user-alice.xml @@ -10,184 +10,46 @@ xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:m="http://midpoint.evolveum.com/xml/ns/public/common/extension-metadata-3" oid="9fc389be-5b47-4e9d-90b5-33fffd87b3ca"> - alice - - - - name - - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - name - - - - - http://example.com/identity/loa#official - - - - - - - givenName - - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - givenName - - - - - http://example.com/identity/loa#official - - - - - - familyName - - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - familyName - - - - - http://example.com/identity/loa#official - - - - - - honorificSuffix - - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - honorificSuffix - - - - - http://example.com/identity/loa#official - - - - - - nickName - - - - http://midpoint.evolveum.com/data-provenance/source#userAction - alice - - nickName - - - - - http://example.com/identity/loa#basic - - - - - - fullName - - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - givenName - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - familyName - - - http://midpoint.evolveum.com/data-provenance/source#resource - Dummy Resource - - - honorificSuffix - - - http://midpoint.evolveum.com/data-provenance/transformer#mapping - mapping-fullname - - - - - - http://example.com/identity/loa#official - - - - - - organizationalUnit - Development - - - - http://midpoint.evolveum.com/data-provenance/source#userAction - jack - - organizationalUnit - - - - - http://example.com/identity/loa#absolute - - - - - - assignment[111] - - - - http://example.com/identity/loa#none - - - - - http://midpoint.evolveum.com/data-provenance/source#userAction - willTurner - - - - - - - assignment[111]/subtype - manual - + + <_value>alice + <_metadata> + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + name + + + + + http://example.com/identity/loa#official + + + + + + <_metadata> + + + http://example.com/identity/loa#none + + + + + http://midpoint.evolveum.com/data-provenance/source#userAction + willTurner + + + + + experimental + + <_value>manual + <_metadata> http://midpoint.evolveum.com/data-provenance/source#userAction @@ -195,25 +57,19 @@ - - - - assignment[111]/activation/administrativeStatus - - - - Wait a moment, Alice! - - - - - - - experimental - manual + + - disabled + + <_value>disabled + <_metadata> + + + Wait a moment, Alice! + + + @@ -223,10 +79,118 @@ disabled - Alice - Green - Ph.D. - Alice Green, Ph.D. + + <_value>Alice + <_metadata> + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + givenName + + + + + http://example.com/identity/loa#official + + + + + + <_value>Green + <_metadata> + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + familyName + + + + + http://example.com/identity/loa#official + + + + + + <_value>Ph.D. + <_metadata> + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + honorificSuffix + + + + + http://example.com/identity/loa#official + + + + + + <_value>Alice Green, Ph.D. + <_metadata> + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + givenName + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + familyName + + + http://midpoint.evolveum.com/data-provenance/source#resource + Dummy Resource + + + honorificSuffix + + + http://midpoint.evolveum.com/data-provenance/transformer#mapping + mapping-fullname + + + + + + http://example.com/identity/loa#official + + + + Operations - Development + + <_value>Development + <_metadata> + + + http://midpoint.evolveum.com/data-provenance/source#userAction + jack + + organizationalUnit + + + + + http://example.com/identity/loa#absolute + + + + diff --git a/model/model-intest/src/test/resources/metadata/user-bob.xml b/model/model-intest/src/test/resources/metadata/user-bob.xml index 9a9b18ff2b6..4367f7ffff3 100644 --- a/model/model-intest/src/test/resources/metadata/user-bob.xml +++ b/model/model-intest/src/test/resources/metadata/user-bob.xml @@ -7,29 +7,24 @@ bob - - - givenName - - - low - - - - - familyName - - - high - - - - regular - Bob - Green + + <_value>Bob + <_metadata> + + low + + + + + <_value>Green + <_metadata> + + high + + + diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java index 6b7c39dd9f2..3d73e00cdeb 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java @@ -599,7 +599,6 @@ private PrismObject updateLoadedObject(GetObjectResult validateObjectType(prismObject, type); ObjectTypeUtil.normalizeAllRelations(prismObject, relationRegistry); - prismObject.fixMockUpValueMetadata(); return prismObject; } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java index 4b0ce05a722..5a12aadd497 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerQuartzImpl.java @@ -120,6 +120,7 @@ public class TaskManagerQuartzImpl implements TaskManager, BeanFactoryAware, Sys @Autowired private Tracer tracer; @Autowired private CacheDispatcher cacheDispatcher; @Autowired private CacheRegistry cacheRegistry; + @Autowired private CounterManager counterManager; private GlobalTracingOverride globalTracingOverride; @@ -2721,4 +2722,8 @@ public Collection getLocalNodeGroups() { public CacheRegistry getCacheRegistry() { return cacheRegistry; } + + public CounterManager getCounterManager() { + return counterManager; + } } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/JobExecutor.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/JobExecutor.java index dc546f9754f..8faa287b211 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/JobExecutor.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/JobExecutor.java @@ -725,7 +725,13 @@ private TaskRunResult executeHandler(TaskHandler handler, OperationResult execut task.setResultTransient(task.createUnnamedTaskResult()); } - TaskRunResult runResult = taskManagerImpl.getHandlerExecutor().executeHandler(task, null, handler, executionResult); + TaskRunResult runResult; + try { + runResult = taskManagerImpl.getHandlerExecutor().executeHandler(task, null, handler, executionResult); + } finally { + // TEMPORARY see MID-6343; TODO implement correctly! + taskManagerImpl.getCounterManager().cleanupCounters(task.getOid()); + } // It is dangerous to start waiting for transient children if they were not told to finish! Make sure you signal them // to stop at appropriate place. diff --git a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/RestService.java b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/RestService.java index 30e8708a455..427ac768d20 100644 --- a/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/RestService.java +++ b/tools/ninja/src/main/java/com/evolveum/midpoint/ninja/impl/RestService.java @@ -14,6 +14,7 @@ import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.ninja.util.BasicAuthenticationInterceptor; import com.evolveum.midpoint.ninja.util.LoggingInterceptor; @@ -29,6 +30,7 @@ import javax.xml.namespace.QName; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -69,14 +71,17 @@ public RestService(String url, String username, String password) { @Override protected void registerExtensionSchemas(SchemaRegistryImpl schemaRegistry) throws SchemaException, FileNotFoundException { - super.registerExtensionSchemas(schemaRegistry); - + try { + super.registerExtensionSchemas(schemaRegistry); + } catch (IOException e) { + throw new SystemException(e); + } RestService.this.registerExtensionSchemas(schemaRegistry); } }; return factory.createPrismContext(); - } catch (SchemaException | FileNotFoundException ex) { + } catch (SchemaException | IOException ex) { throw new NinjaException("Couldn't load prism context", ex); } });