/*
* 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.model.impl.util;
import com.evolveum.midpoint.model.api.validator.Issue;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.util.QNameUtil;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.List;
/**
* EXPERIMENTAL
*
* @author mederly
*/
public class DataModelUtil {
private static final Trace LOGGER = TraceManager.getTrace(DataModelUtil.class);
public static final String CAT_ITEM_PATH = "itemPath";
public static final String C_DOES_NOT_START_WITH_NAME = "itemPath";
public static final String C_NO_DEFAULT_VARIABLE = "noDefaultVariable";
public static final String C_ILLEGAL_USE_OF_ACCOUNT_VARIABLE = "cannotUseAccountVariable";
public static final String C_NO_OBJECT_DEFINITION = "noObjectDefinition";
public static class PathResolutionResult {
@NotNull private List<Issue> issues = new ArrayList<>();
private final ItemDefinition<?> definition;
public PathResolutionResult(ItemDefinition<?> definition) {
this.definition = definition;
}
public PathResolutionResult(@NotNull Issue issue) {
this.definition = null;
issues.add(issue);
}
@NotNull
public List<Issue> getIssues() {
return issues;
}
public ItemDefinition<?> getDefinition() {
return definition;
}
}
public static class PathResolutionContext {
@NotNull PrismContext prismContext;
QName defaultVariable;
public PathResolutionContext(@NotNull PrismContext prismContext, QName defaultVariable) {
this.prismContext = prismContext;
this.defaultVariable = defaultVariable;
}
}
public static class ResourceResolutionContext extends PathResolutionContext {
@NotNull ResourceType resource;
@NotNull ShadowKindType kind;
@NotNull String intent;
public ResourceResolutionContext(@NotNull PrismContext prismContext, QName defaultVariable, @NotNull ResourceType resource,
@NotNull ShadowKindType kind, @NotNull String intent) {
super(prismContext, defaultVariable);
this.resource = resource;
this.kind = kind;
this.intent = intent;
}
}
@Nullable // null means not supported yet
@SuppressWarnings("unchecked")
public static PathResolutionResult resolvePath(@NotNull ItemPath path, @NotNull PathResolutionContext context) {
if (!(path.first() instanceof NameItemPathSegment)) {
return new PathResolutionResult(new Issue(Issue.Severity.WARNING, CAT_ITEM_PATH, C_DOES_NOT_START_WITH_NAME,
"Path does not start with a name: '" + path + "'", null, null));
}
QName varName;
ItemPath itemPath;
NameItemPathSegment firstNameSegment = (NameItemPathSegment) path.first();
if (firstNameSegment.isVariable()) {
varName = firstNameSegment.getName();
itemPath = path.tail();
} else {
if (context.defaultVariable == null) {
return new PathResolutionResult(new Issue(Issue.Severity.WARNING, CAT_ITEM_PATH, C_NO_DEFAULT_VARIABLE,
"No default variable for item path: '" + path + "'", null, null));
}
varName = context.defaultVariable;
itemPath = path;
}
if (QNameUtil.match(ExpressionConstants.VAR_ACCOUNT, varName)) {
if (!(context instanceof ResourceResolutionContext)) {
return new PathResolutionResult(new Issue(Issue.Severity.WARNING, CAT_ITEM_PATH, C_ILLEGAL_USE_OF_ACCOUNT_VARIABLE,
"Illegal use of 'account' variable: '" + path + "'", null, null));
} else {
// TODO implement checking of $account-based paths
return null;
}
} else if (QNameUtil.match(ExpressionConstants.VAR_USER, varName) || QNameUtil.match(ExpressionConstants.VAR_FOCUS, varName)) {
Class<? extends FocusType> clazz = FocusType.class;
if (context instanceof ResourceResolutionContext) {
ResourceResolutionContext rctx = (ResourceResolutionContext) context;
ObjectSynchronizationType def = ResourceTypeUtil.findObjectSynchronization(rctx.resource, rctx.kind, rctx.intent);
if (def != null) {
QName focusType = def.getFocusType() != null ? def.getFocusType() : UserType.COMPLEX_TYPE;
PrismObjectDefinition<Objectable> objdef = rctx.prismContext.getSchemaRegistry().findObjectDefinitionByType(focusType);
if (objdef != null && objdef.getCompileTimeClass() != null && FocusType.class.isAssignableFrom(objdef.getCompileTimeClass())) {
clazz = (Class) objdef.getCompileTimeClass();
}
} else {
clazz = UserType.class; // the default (MID-3307)
}
}
return resolvePathForType(clazz, itemPath, context);
} else if (QNameUtil.match(ExpressionConstants.VAR_ACTOR, varName)) {
return resolvePathForType(UserType.class, itemPath, context);
} else if (QNameUtil.match(ExpressionConstants.VAR_INPUT, varName)) {
return null;
} else {
return null; // TODO list all possible variables here and issue a warning if no one matches
}
}
@Nullable // null means not supported yet
public static PathResolutionResult resolvePathForType(@NotNull Class<? extends ObjectType> clazz, @NotNull ItemPath path, @NotNull PathResolutionContext context) {
PrismObjectDefinition<?> def = context.prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(clazz);
if (def == null) {
return new PathResolutionResult(new Issue(Issue.Severity.WARNING, CAT_ITEM_PATH, C_NO_OBJECT_DEFINITION,
"No definition for " + clazz + " in item path: '" + path + "'", null, null));
}
ItemDefinition<?> itemDef = def.findItemDefinition(path);
if (itemDef != null) {
return new PathResolutionResult(itemDef);
} else {
if (FocusType.class.equals(clazz) && context instanceof ResourceResolutionContext) {
ResourceResolutionContext rctx = (ResourceResolutionContext) context;
return new PathResolutionResult(new Issue(Issue.Severity.INFO, CAT_ITEM_PATH, C_NO_OBJECT_DEFINITION,
"Couldn't verify item path '" + path + "' because specific focus type (user, role, org, ...) is not defined for kind=" + rctx.kind + ", intent=" + rctx.intent, null, null));
} else {
return new PathResolutionResult(new Issue(Issue.Severity.WARNING, CAT_ITEM_PATH, C_NO_OBJECT_DEFINITION,
"No definition for '" + path + "' in " + def.getName().getLocalPart(), null, null));
}
}
}
}