Skip to content

Commit

Permalink
Optimized canonicalized ItemPath format a bit. Fixed NPE in audit log…
Browse files Browse the repository at this point in the history
… viewer.
  • Loading branch information
mederly committed Dec 13, 2016
1 parent ad487ab commit 6f51b93
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 68 deletions.
Expand Up @@ -86,37 +86,29 @@ private Map<String, ItemDefinition<?>> collectAvailableDefinitions(String input)
ItemPathDto parentItem = getModelObject().getParentPath();
if (parentItem != null) {
if (parentItem.getItemDef() instanceof PrismContainerDefinition<?>) {
PrismContainerDefinition<?> parentContainer = (PrismContainerDefinition<?>) parentItem
.getItemDef();
for (ItemDefinition<?> def : parentContainer.getDefinitions()) {
if (def.getName() != null) {
if (StringUtils.isBlank(input)) {
toSelect.put(def.getName().getLocalPart(), def);
} else {
if (def.getName().getLocalPart().startsWith(input)) {
toSelect.put(def.getName().getLocalPart(), def);
}
}
}
}
PrismContainerDefinition<?> parentContainer = (PrismContainerDefinition<?>) parentItem.getItemDef();
collectItems(parentContainer.getDefinitions(), input, toSelect);
}
} else {
Collection<ItemDefinition<?>> definitions = getSchemaDefinitionMap().get(getModelObject().getObjectType());
collectItems(definitions, input, toSelect);
}
return toSelect;
}

for (ItemDefinition<?> def : getSchemaDefinitionMap().get(getModelObject().getObjectType())) {
if (def.getName() != null) {
if (StringUtils.isBlank(input)) {
toSelect.put(def.getName().getLocalPart(), def);
} else {
if (def.getName().getLocalPart().startsWith(input)) {
toSelect.put(def.getName().getLocalPart(), def);
}
}
private void collectItems(Collection<? extends ItemDefinition> definitions, String input, Map<String, ItemDefinition<?>> toSelect) {
if (definitions == null) {
return;
}
for (ItemDefinition<?> def : definitions) {
if (StringUtils.isBlank(input)) {
toSelect.put(def.getName().getLocalPart(), def);
} else {
if (def.getName().getLocalPart().startsWith(input)) {
toSelect.put(def.getName().getLocalPart(), def);
}
}

}
// }
return toSelect;
}

public void refreshModel(ItemPathDto newModel) {
Expand Down
Expand Up @@ -6,10 +6,9 @@
import com.evolveum.midpoint.gui.api.page.PageBase;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils;
import com.evolveum.midpoint.prism.marshaller.XPathHolder;
import com.evolveum.midpoint.prism.path.CanonicalItemPath;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.util.QNameUtil;
Expand Down Expand Up @@ -38,9 +37,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
Expand Down Expand Up @@ -413,8 +410,7 @@ public Map<String, Object> getParameters() {
}
if (search.getChangedItem().toItemPath() != null) {
ItemPath itemPath = search.getChangedItem().toItemPath();
XPathHolder holder = new XPathHolder(itemPath);
parameters.put("changedItem", holder.toCanonicalPath(null, getPrismContext()));
parameters.put("changedItem", CanonicalItemPath.create(itemPath).asString());
}
parameters.put("eventType", search.getEventType());
parameters.put("eventStage", search.getEventStage());
Expand Down
@@ -0,0 +1,149 @@
/*
* Copyright (c) 2010-2016 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.evolveum.midpoint.prism.path;

import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.util.QNameUtil;

import javax.xml.namespace.QName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* @author katkav
* @author mederly
*/
public class CanonicalItemPath implements Serializable {

private static final String SHORTCUT_MARKER = "$";

// currently we support only named segments in canonical paths
public static class Segment implements Serializable {
private final QName name;
private final Integer index; // N means this is Nth unique non-empty namespace in the path (starting from 0)
private final Integer shortcut; // K means this namespace is the same as the one with index=K (null if 1st occurrence)
private Segment(QName name, Integer index, Integer shortcut) {
this.name = name;
this.index = index;
this.shortcut = shortcut;
}
public QName getName() {
return name;
}
public Integer getIndex() {
return index;
}
public Integer getShortcut() {
return shortcut;
}
}

private final List<Segment> segments = new ArrayList<>();

public static CanonicalItemPath create(ItemPath itemPath, Class<? extends Containerable> clazz, PrismContext prismContext) {
return new CanonicalItemPath(itemPath, clazz, prismContext);
}

public static CanonicalItemPath create(ItemPath itemPath) {
return new CanonicalItemPath(itemPath, null, null);
}

private CanonicalItemPath(ItemPath itemPath, Class<? extends Containerable> clazz, PrismContext prismContext) {
ItemDefinition def = clazz != null && prismContext != null ?
prismContext.getSchemaRegistry().findContainerDefinitionByCompileTimeClass(clazz) : null;
while (!ItemPath.isNullOrEmpty(itemPath)) {
ItemPathSegment first = itemPath.first();
if (first instanceof NameItemPathSegment) {
// TODO what about variable named item path segments?
QName name = ((NameItemPathSegment) first).getName();
if (def instanceof PrismContainerDefinition) {
def = ((PrismContainerDefinition) def).findItemDefinition(name);
if (def != null && !QNameUtil.hasNamespace(name)) {
name = def.getName();
}
}
addToSegments(name);
} else if (first instanceof IdItemPathSegment) {
// ignored (for now)
} else {
throw new UnsupportedOperationException("Canonicalization of non-name/non-ID segments is not supported: " + first);
}
itemPath = itemPath.rest();
}
}

private void addToSegments(QName name) {
if (!QNameUtil.hasNamespace(name)) {
segments.add(new Segment(name, null, null));
return;
}
String namespace = name.getNamespaceURI();
int index = 0;
Integer shortcut = null;
for (Segment segment : segments) {
if (namespace.equals(segment.name.getNamespaceURI())) {
shortcut = index = segment.index;
break;
}
if (QNameUtil.hasNamespace(segment.name) && segment.shortcut == null) {
// we found a unique non-empty namespace! (so increase the index)
index++;
}
}
segments.add(new Segment(name, index, shortcut));
}

public List<Segment> getSegments() {
return segments;
}

public int size() {
return segments.size();
}

public String asString(int howManySegments) {
StringBuilder sb = new StringBuilder();
Iterator<Segment> iterator = segments.iterator();
while (iterator.hasNext() && howManySegments > 0) {
Segment segment = iterator.next();
howManySegments--;
sb.append("\\");
if (segment.shortcut == null) { // always true for unqualified names
sb.append(QNameUtil.qNameToUri(segment.name));
} else {
sb.append(SHORTCUT_MARKER).append(segment.shortcut)
.append(QNameUtil.DEFAULT_QNAME_URI_SEPARATOR_CHAR).append(segment.name.getLocalPart());
}
}
return sb.length() == 0 ? "\\" : sb.toString(); // TODO should we really return "\\" on empty path?
}

public String asString() {
return asString(segments.size());
}

@Override
public String toString() {
return asString();
}

}
Expand Up @@ -16,11 +16,16 @@

package com.evolveum.midpoint.schema.test;

import static com.evolveum.midpoint.schema.constants.SchemaConstants.NS_C;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;

import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.path.CanonicalItemPath;

import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
import org.testng.AssertJUnit;
Expand Down Expand Up @@ -372,7 +377,7 @@ public void xpathFromQNameTest() {
@Test
public void testXPathSerializationToDom() {
// GIVEN
QName qname1 = new QName(SchemaConstants.NS_C, "extension");
QName qname1 = new QName(NS_C, "extension");
QName qname2 = new QName(NS_FOO, "foo");
XPathHolder xPathHolder1 = new XPathHolder(qname1, qname2);
QName elementQName = new QName(NS_BAR, "bar");
Expand Down Expand Up @@ -427,4 +432,97 @@ public void testUndefinedPrefix() throws ParserConfigurationException, SAXExcept

}

@Test
public void testCanonicalizationEmpty() throws Exception {
assertCanonical(null, null, "\\");
assertCanonical(ItemPath.EMPTY_PATH, null, "\\");
}

@Test
public void testCanonicalizationSimple() throws Exception {
ItemPath path = new ItemPath(UserType.F_NAME);
assertCanonical(path, null, "\\" + NS_C + "#name");
}

@Test
public void testCanonicalizationSimpleNoNs() throws Exception {
ItemPath path = new ItemPath(UserType.F_NAME.getLocalPart());
assertCanonical(path, null, "\\#name");
assertCanonical(path, UserType.class, "\\" + NS_C + "#name");
}

@Test
public void testCanonicalizationMulti() throws Exception {
ItemPath path = new ItemPath(UserType.F_ASSIGNMENT, 1234, AssignmentType.F_ACTIVATION,
ActivationType.F_ADMINISTRATIVE_STATUS);
assertCanonical(path, null, "\\" + NS_C + "#assignment",
"\\" + NS_C + "#assignment\\$0#activation",
"\\" + NS_C + "#assignment\\$0#activation\\$0#administrativeStatus");
}

@Test
public void testCanonicalizationMultiNoNs() throws Exception {
ItemPath path = new ItemPath(UserType.F_ASSIGNMENT.getLocalPart(), 1234, AssignmentType.F_ACTIVATION.getLocalPart(),
ActivationType.F_ADMINISTRATIVE_STATUS.getLocalPart());
assertCanonical(path, null, "\\#assignment",
"\\#assignment\\#activation", "\\#assignment\\#activation\\#administrativeStatus");
assertCanonical(path, UserType.class, "\\" + NS_C + "#assignment",
"\\" + NS_C + "#assignment\\$0#activation",
"\\" + NS_C + "#assignment\\$0#activation\\$0#administrativeStatus");
}

@Test
public void testCanonicalizationMixedNs() throws Exception {
ItemPath path = new ItemPath(UserType.F_ASSIGNMENT.getLocalPart(), 1234, AssignmentType.F_EXTENSION,
new QName("http://piracy.org/inventory", "store"),
new QName("http://piracy.org/inventory", "shelf"),
new QName("x"), ActivationType.F_ADMINISTRATIVE_STATUS);
assertCanonical(path, null,
"\\#assignment",
"\\#assignment\\" + NS_C + "#extension",
"\\#assignment\\" + NS_C + "#extension\\http://piracy.org/inventory#store",
"\\#assignment\\" + NS_C + "#extension\\http://piracy.org/inventory#store\\$1#shelf",
"\\#assignment\\" + NS_C + "#extension\\http://piracy.org/inventory#store\\$1#shelf\\#x",
"\\#assignment\\" + NS_C + "#extension\\http://piracy.org/inventory#store\\$1#shelf\\#x\\$0#administrativeStatus");
assertCanonical(path, UserType.class,
"\\" + NS_C + "#assignment",
"\\" + NS_C + "#assignment\\$0#extension",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store\\$1#shelf",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store\\$1#shelf\\#x",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store\\$1#shelf\\#x\\$0#administrativeStatus");
}

@Test
public void testCanonicalizationMixedNs2() throws Exception {
ItemPath path = new ItemPath(UserType.F_ASSIGNMENT.getLocalPart(), 1234, AssignmentType.F_EXTENSION.getLocalPart(),
new QName("http://piracy.org/inventory", "store"),
new QName("http://piracy.org/inventory", "shelf"),
AssignmentType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS);
assertCanonical(path, null,
"\\#assignment",
"\\#assignment\\#extension",
"\\#assignment\\#extension\\http://piracy.org/inventory#store",
"\\#assignment\\#extension\\http://piracy.org/inventory#store\\$0#shelf",
"\\#assignment\\#extension\\http://piracy.org/inventory#store\\$0#shelf\\" + NS_C + "#activation",
"\\#assignment\\#extension\\http://piracy.org/inventory#store\\$0#shelf\\" + NS_C + "#activation\\$1#administrativeStatus");
assertCanonical(path, UserType.class,
"\\" + NS_C + "#assignment",
"\\" + NS_C + "#assignment\\$0#extension",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store\\$1#shelf",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store\\$1#shelf\\$0#activation",
"\\" + NS_C + "#assignment\\$0#extension\\http://piracy.org/inventory#store\\$1#shelf\\$0#activation\\$0#administrativeStatus");
}

private void assertCanonical(ItemPath path, Class<? extends Containerable> clazz, String... representations) {
CanonicalItemPath canonicalItemPath = CanonicalItemPath.create(path, clazz, PrismTestUtil.getPrismContext());
System.out.println(path + " => " + canonicalItemPath.asString() + " (" + clazz + ")");
for (int i = 0; i < representations.length; i++) {
String c = canonicalItemPath.asString(i+1);
assertEquals("Wrong string representation of length " + (i+1), representations[i], c);
}
assertEquals("Wrong string representation ", representations[representations.length-1], canonicalItemPath.asString());
}

}
Expand Up @@ -44,12 +44,13 @@ public class QNameUtil {

public static final Trace LOGGER = TraceManager.getTrace(QNameUtil.class);

// TODO consider where to put all this undeclared-prefixes-things
// TODO consider where to put all this undeclared-prefixes-things
// Hopefully in 3.2 everything will find its place

private static final String UNDECLARED_PREFIX_MARK = "__UNDECLARED__";
public static final char DEFAULT_QNAME_URI_SEPARATOR_CHAR = '#';

// Whether we want to tolerate undeclared XML prefixes in QNames
// Whether we want to tolerate undeclared XML prefixes in QNames
// This is here only for backward compatibility with versions 3.0-3.1.
// Will be set to false starting with 3.2 (MID-2191)
private static boolean tolerateUndeclaredPrefixes = false;
Expand All @@ -63,7 +64,7 @@ public static String qNameToUri(QName qname) {
}

public static String qNameToUri(QName qname, boolean unqualifiedStartsWithHash) {
return qNameToUri(qname, unqualifiedStartsWithHash, '#');
return qNameToUri(qname, unqualifiedStartsWithHash, DEFAULT_QNAME_URI_SEPARATOR_CHAR);
}

public static String qNameToUri(QName qname, boolean unqualifiedStartsWithHash, char separatorChar) {
Expand Down

0 comments on commit 6f51b93

Please sign in to comment.