/*
* 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.web.component.prism;
import com.evolveum.midpoint.common.refinery.*;
import com.evolveum.midpoint.gui.api.component.autocomplete.AutoCompleteTextPanel;
import com.evolveum.midpoint.gui.api.component.password.PasswordPanel;
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.*;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.schema.DeltaConvertor;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.task.api.Task;
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.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.LockoutStatusPanel;
import com.evolveum.midpoint.web.component.form.ValueChoosePanel;
import com.evolveum.midpoint.web.component.input.*;
import com.evolveum.midpoint.web.component.model.delta.DeltaDto;
import com.evolveum.midpoint.web.component.model.delta.ModificationsPanel;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.model.LookupPropertyModel;
import com.evolveum.midpoint.web.page.admin.users.PageUser;
import com.evolveum.midpoint.web.page.admin.users.component.AssociationValueChoicePanel;
import com.evolveum.midpoint.web.security.SecurityUtils;
import com.evolveum.midpoint.web.util.DateValidator;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.query_3.QueryType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.Validate;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteTextField;
import org.apache.wicket.extensions.yui.calendar.DateTimeField;
import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* @author lazyman
*/
public class PrismValuePanel extends Panel {
private static final long serialVersionUID = 1L;
private static final String ID_FEEDBACK = "feedback";
private static final String ID_INPUT = "input";
private static final String ID_ADD_BUTTON = "addButton";
private static final String ID_REMOVE_BUTTON = "removeButton";
private static final String ID_VALUE_CONTAINER = "valueContainer";
private static final Trace LOGGER = TraceManager.getTrace(PrismValuePanel.class);
private IModel<ValueWrapper> valueWrapperModel;
private PageBase pageBase;
public PrismValuePanel(String id, IModel<ValueWrapper> valueWrapperModel, IModel<String> labelModel, Form form,
String valueCssClass, String inputCssClass, PageBase pageBase){
super(id);
Validate.notNull(valueWrapperModel, "Property value model must not be null.");
Validate.notNull(pageBase, "The reference to page base must not be null.");
this.pageBase = pageBase;
this.valueWrapperModel = valueWrapperModel;
initLayout(labelModel, form, valueCssClass, inputCssClass);
}
private void initLayout(IModel<String> labelModel, Form form, String valueCssClass, String inputCssClass) {
//container
WebMarkupContainer valueContainer = new WebMarkupContainer(ID_VALUE_CONTAINER);
valueContainer.setOutputMarkupId(true);
valueContainer.add(new AttributeModifier("class", valueCssClass));
add(valueContainer);
//feedback
FeedbackPanel feedback = new FeedbackPanel(ID_FEEDBACK);
feedback.setOutputMarkupId(true);
add(feedback);
//input
Panel input = createInputComponent(ID_INPUT, labelModel, form);
input.add(new AttributeModifier("class", inputCssClass));
if (input instanceof InputPanel) {
initAccessBehaviour((InputPanel) input);
feedback.setFilter(new ComponentFeedbackMessageFilter(((InputPanel) input).getBaseFormComponent()));
} else if (input instanceof LockoutStatusPanel) {
feedback.setFilter(new ComponentFeedbackMessageFilter(input));
} else if (input instanceof ValueChoosePanel) {
feedback.setFilter(new ComponentFeedbackMessageFilter(input));
} else if (input instanceof AssociationValueChoicePanel){
feedback.setFilter(new ComponentFeedbackMessageFilter(((AssociationValueChoicePanel) input).getTextComponent()));
}
valueContainer.add(input);
//buttons
AjaxLink addButton = new AjaxLink(ID_ADD_BUTTON) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
addValue(target);
}
};
addButton.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return isAddButtonVisible();
}
});
valueContainer.add(addButton);
AjaxLink removeButton = new AjaxLink(ID_REMOVE_BUTTON) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
removeValue(target);
}
};
removeButton.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return isRemoveButtonVisible();
}
});
valueContainer.add(removeButton);
}
private IModel<String> createHelpModel() {
return new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
@Override
public String getObject() {
ItemWrapper wrapper = valueWrapperModel.getObject().getItem();
return wrapper.getItem().getHelp();
}
};
}
private boolean isAccessible(ItemDefinition def, ContainerStatus status) {
switch (status) {
case ADDING:
if (!def.canAdd()) {
return false;
}
break;
case MODIFYING:
if (!def.canModify()) {
return false;
}
break;
}
return true;
}
private void initAccessBehaviour(InputPanel panel) {
List<FormComponent> components = panel.getFormComponents();
for (FormComponent component : components) {
component.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isEnabled() {
ValueWrapper wrapper = valueWrapperModel.getObject();
ItemWrapper itemWrapper = wrapper.getItem();
if (valueWrapperModel.getObject().isReadonly()) {
return false;
}
if (itemWrapper.getContainer() == null) {
return true; // TODO
}
ObjectWrapper object = itemWrapper.getContainer().getObject();
ItemDefinition def = itemWrapper.getItem().getDefinition();
return object == null || isAccessible(def, object.getStatus());
}
});
}
}
private int countUsableValues(ItemWrapper<? extends Item, ? extends ItemDefinition> property) {
int count = 0;
for (ValueWrapper value : property.getValues()) {
value.normalize(property.getItemDefinition().getPrismContext());
if (ValueStatus.DELETED.equals(value.getStatus())) {
continue;
}
if (ValueStatus.ADDED.equals(value.getStatus()) && !value.hasValueChanged()) {
continue;
}
count++;
}
return count;
}
private List<ValueWrapper> getUsableValues(ItemWrapper<? extends Item, ? extends ItemDefinition> property) {
List<ValueWrapper> values = new ArrayList<>();
for (ValueWrapper value : property.getValues()) {
value.normalize(property.getItemDefinition().getPrismContext());
if (ValueStatus.DELETED.equals(value.getStatus())) {
continue;
}
values.add(value);
}
return values;
}
private int countNonDeletedValues(ItemWrapper<? extends Item, ? extends ItemDefinition> property) {
int count = 0;
for (ValueWrapper value : property.getValues()) {
value.normalize(property.getItemDefinition().getPrismContext());
if (ValueStatus.DELETED.equals(value.getStatus())) {
continue;
}
count++;
}
return count;
}
private boolean hasEmptyPlaceholder(ItemWrapper<? extends Item, ? extends ItemDefinition> property) {
for (ValueWrapper value : property.getValues()) {
value.normalize(property.getItemDefinition().getPrismContext());
if (ValueStatus.ADDED.equals(value.getStatus()) && !value.hasValueChanged()) {
return true;
}
}
return false;
}
private boolean isRemoveButtonVisible() {
ValueWrapper valueWrapper = valueWrapperModel.getObject();
if (valueWrapper.isReadonly()){
return false;
}
Component inputPanel = this.get(ID_VALUE_CONTAINER).get(ID_INPUT);
if (inputPanel instanceof ValueChoosePanel || inputPanel instanceof AssociationValueChoicePanel){
return true;
}
ItemWrapper propertyWrapper = valueWrapper.getItem();
ItemDefinition definition = propertyWrapper.getItem().getDefinition();
int min = definition.getMinOccurs();
int count = countNonDeletedValues(propertyWrapper);
if (count <= 1 || count <= min) {
return false;
}
if (propertyWrapper.getContainer() == null) {
return true; // TODO
}
return isAccessible(definition, getContainerStatus(propertyWrapper));
}
private ContainerStatus getContainerStatus(ItemWrapper propertyWrapper) {
final ObjectWrapper objectWrapper = propertyWrapper.getContainer().getObject();
return objectWrapper != null ? objectWrapper.getStatus() : ContainerStatus.MODIFYING;
}
private boolean isAddButtonVisible() {
Component inputPanel = this.get(ID_VALUE_CONTAINER).get(ID_INPUT);
ValueWrapper valueWrapper = valueWrapperModel.getObject();
if (valueWrapper.isReadonly()){
return false;
}
ItemWrapper propertyWrapper = valueWrapper.getItem();
Item property = propertyWrapper.getItem();
ItemDefinition definition = property.getDefinition();
int max = definition.getMaxOccurs();
List<ValueWrapper> usableValues = getUsableValues(propertyWrapper);
if (usableValues.indexOf(valueWrapper) != usableValues.size() - 1) {
return false;
}
if (max == -1) {
return true;
}
if (countNonDeletedValues(propertyWrapper) >= max) {
return false;
}
if (propertyWrapper.getContainer() == null) {
return true; // TODO
}
return isAccessible(definition, getContainerStatus(propertyWrapper));
}
private Panel createInputComponent(String id, IModel<String> labelModel, Form form) {
ValueWrapper valueWrapper = valueWrapperModel.getObject();
ObjectWrapper objectWrapper = null;
if (valueWrapper.getItem().getContainer() != null) {
objectWrapper = valueWrapper.getItem().getContainer().getObject();
}
Item property = valueWrapper.getItem().getItem();
boolean required = property.getDefinition().getMinOccurs() > 0;
Panel component = createTypedInputComponent(id);
if (component instanceof InputPanel) {
InputPanel inputPanel = (InputPanel) component;
//adding valid from/to date range validator, if necessary
ItemPath activation = new ItemPath(UserType.F_ACTIVATION);
if (ActivationType.F_VALID_FROM.equals(property.getElementName())) {
DateValidator validator = WebComponentUtil.getRangeValidator(form, activation);
validator.setDateFrom((DateTimeField) inputPanel.getBaseFormComponent());
} else if (ActivationType.F_VALID_TO.equals(property.getElementName())) {
DateValidator validator = WebComponentUtil.getRangeValidator(form, activation);
validator.setDateTo((DateTimeField) inputPanel.getBaseFormComponent());
}
final List<FormComponent> formComponents = inputPanel.getFormComponents();
for (FormComponent formComponent : formComponents) {
formComponent.setLabel(labelModel);
formComponent.setRequired(required);
if (formComponent instanceof TextField) {
formComponent.add(new AttributeModifier("size", "42"));
}
formComponent.add(new AjaxFormComponentUpdatingBehavior("blur") {
@Override
protected void onUpdate(AjaxRequestTarget target) {
}
});
// Validation occurs when submitting the form
// if (form != null) {
// AjaxFormValidatingBehavior validator = new AjaxFormValidatingBehavior(form, "Blur");
//
// formComponent.add(validator);
// }
}
}
if (component == null) {
throw new RuntimeException("Cannot create input component for item "+property+" ("+valueWrapper+") in "+objectWrapper);
}
return component;
}
// normally this method returns an InputPanel;
// however, for some special readonly types (like ObjectDeltaType) it will return a Panel
private Panel createTypedInputComponent(String id) {
// ValueWrapper valueWrapper = model.getObject();
// ItemWrapper itemWrapper =
final Item item = valueWrapperModel.getObject().getItem().getItem();
Panel panel = null;
if (item instanceof PrismProperty) {
final PrismProperty property = (PrismProperty) item;
PrismPropertyDefinition definition = property.getDefinition();
final QName valueType = definition.getTypeName();
final String baseExpression = "value.value"; //pointing to prism property real value
//fixing MID-1230, will be improved with some kind of annotation or something like that
//now it works only in description
if (ObjectType.F_DESCRIPTION.equals(definition.getName())) {
return new TextAreaPanel(id, new PropertyModel(valueWrapperModel, baseExpression), null);
}
if (ActivationType.F_ADMINISTRATIVE_STATUS.equals(definition.getName())) {
return WebComponentUtil.createEnumPanel(ActivationStatusType.class, id, new PropertyModel<ActivationStatusType>(valueWrapperModel, baseExpression), this);
} else if(ActivationType.F_LOCKOUT_STATUS.equals(definition.getName())){
return new LockoutStatusPanel(id, valueWrapperModel.getObject(), new PropertyModel<LockoutStatusType>(valueWrapperModel, baseExpression));
} else {
if (definition.getTypeName().getLocalPart().equals(ActivationStatusType.class.getSimpleName())) {
return WebComponentUtil.createEnumPanel(ActivationStatusType.class, id, new PropertyModel<ActivationStatusType>(valueWrapperModel, baseExpression), this);
}
}
if (DOMUtil.XSD_DATETIME.equals(valueType)) {
panel = new DatePanel(id, new PropertyModel<XMLGregorianCalendar>(valueWrapperModel, baseExpression));
} else if (ProtectedStringType.COMPLEX_TYPE.equals(valueType)) {
boolean showRemovePasswordButton = true;
if (pageBase instanceof PageUser &&
((PageUser) pageBase).getObjectWrapper().getObject() != null &&
((PageUser) pageBase).getObjectWrapper().getObject().getOid() != null &&
((PageUser) pageBase).getObjectWrapper().getObject().getOid().equals(SecurityUtils.getPrincipalUser().getOid())) {
showRemovePasswordButton = false;
}
panel = new PasswordPanel(id, new PropertyModel<ProtectedStringType>(valueWrapperModel, baseExpression),
valueWrapperModel.getObject().isReadonly(), showRemovePasswordButton);
} else if (DOMUtil.XSD_BOOLEAN.equals(valueType)) {
panel = new TriStateComboPanel(id, new PropertyModel<Boolean>(valueWrapperModel, baseExpression));
} else if (SchemaConstants.T_POLY_STRING_TYPE.equals(valueType)) {
InputPanel inputPanel;
PrismPropertyDefinition def = property.getDefinition();
if(def.getValueEnumerationRef() != null){
PrismReferenceValue valueEnumerationRef = def.getValueEnumerationRef();
String lookupTableUid = valueEnumerationRef.getOid();
Task task = pageBase.createSimpleTask("loadLookupTable");
OperationResult result = task.getResult();
Collection<SelectorOptions<GetOperationOptions>> options = WebModelServiceUtils.createLookupTableRetrieveOptions();
final PrismObject<LookupTableType> lookupTable = WebModelServiceUtils.loadObject(LookupTableType.class,
lookupTableUid, options, pageBase, task, result);
if (lookupTable != null) {
inputPanel = new AutoCompleteTextPanel<String>(id, new LookupPropertyModel<String>(valueWrapperModel, baseExpression + ".orig",
lookupTable.asObjectable()), String.class) {
@Override
public Iterator<String> getIterator(String input) {
return prepareAutoCompleteList(input, lookupTable).iterator();
}
};
} else {
inputPanel = new TextPanel<>(id, new PropertyModel<String>(valueWrapperModel, baseExpression + ".orig"), String.class);
}
} else {
inputPanel = new TextPanel<>(id, new PropertyModel<String>(valueWrapperModel, baseExpression + ".orig"), String.class);
}
if (ObjectType.F_NAME.equals(def.getName()) || UserType.F_FULL_NAME.equals(def.getName())) {
inputPanel.getBaseFormComponent().setRequired(true);
}
panel = inputPanel;
} else if(DOMUtil.XSD_BASE64BINARY.equals(valueType)) {
panel = new UploadDownloadPanel(id, valueWrapperModel.getObject().isReadonly()){
@Override
public InputStream getStream() {
Object object = ((PrismPropertyValue) valueWrapperModel.getObject().getValue()).getValue();
return object != null ? new ByteArrayInputStream((byte[]) object) : new ByteArrayInputStream(new byte[0]);
// return super.getStream();
}
@Override
public void updateValue(byte[] file) {
((PrismPropertyValue) valueWrapperModel.getObject().getValue()).setValue(file);
}
@Override
public void uploadFilePerformed(AjaxRequestTarget target) {
super.uploadFilePerformed(target);
target.add(PrismValuePanel.this.get(ID_FEEDBACK));
}
@Override
public void removeFilePerformed(AjaxRequestTarget target) {
super.removeFilePerformed(target);
target.add(PrismValuePanel.this.get(ID_FEEDBACK));
}
@Override
public void uploadFileFailed(AjaxRequestTarget target) {
super.uploadFileFailed(target);
target.add(PrismValuePanel.this.get(ID_FEEDBACK));
target.add(((PageBase) getPage()).getFeedbackPanel());
}
};
} else if (ObjectDeltaType.COMPLEX_TYPE.equals(valueType)) {
panel = new ModificationsPanel(id, new AbstractReadOnlyModel<DeltaDto>() {
@Override
public DeltaDto getObject() {
if (valueWrapperModel.getObject() == null || valueWrapperModel.getObject().getValue() == null || ((PrismPropertyValue) valueWrapperModel.getObject().getValue()).getValue() == null) {
return null;
}
PrismContext prismContext = ((PageBase) getPage()).getPrismContext();
ObjectDeltaType objectDeltaType = (ObjectDeltaType) ((PrismPropertyValue) valueWrapperModel.getObject().getValue()).getValue();
try {
ObjectDelta delta = DeltaConvertor.createObjectDelta(objectDeltaType, prismContext);
return new DeltaDto(delta);
} catch (SchemaException e) {
throw new IllegalStateException("Couldn't convert object delta: " + objectDeltaType);
}
}
});
} else if (QueryType.COMPLEX_TYPE.equals(valueType) || CleanupPoliciesType.COMPLEX_TYPE.equals(valueType)) {
return new TextAreaPanel(id, new AbstractReadOnlyModel() {
@Override
public Object getObject() {
if (valueWrapperModel.getObject() == null || valueWrapperModel.getObject().getValue() == null) {
return null;
}
PrismPropertyValue ppv = (PrismPropertyValue) valueWrapperModel.getObject().getValue();
if (ppv == null || ppv.getValue() == null) {
return null;
}
QName name = property.getElementName();
if (name == null && property.getDefinition() != null) {
name = property.getDefinition().getName();
}
if (name == null) {
name = SchemaConstants.C_VALUE;
}
PrismContext prismContext = ((PageBase) getPage()).getPrismContext();
try {
return prismContext.xmlSerializer().serializeAnyData(ppv.getValue(), name);
} catch (SchemaException e) {
throw new SystemException("Couldn't serialize property value of type: " + valueType + ": " + e.getMessage(), e);
}
}
}, 10);
} else {
Class type = XsdTypeMapper.getXsdToJavaMapping(valueType);
if (type != null && type.isPrimitive()) {
type = ClassUtils.primitiveToWrapper(type);
}
if (isEnum(property)) {
return WebComponentUtil.createEnumPanel(definition, id, new PropertyModel<>(valueWrapperModel, baseExpression), this);
}
// // default QName validation is a bit weird, so let's treat QNames as strings [TODO finish this - at the parsing side]
// if (type == QName.class) {
// type = String.class;
// }
PrismPropertyDefinition def = property.getDefinition();
if(def.getValueEnumerationRef() != null){
PrismReferenceValue valueEnumerationRef = def.getValueEnumerationRef();
String lookupTableUid = valueEnumerationRef.getOid();
Task task = pageBase.createSimpleTask("loadLookupTable");
OperationResult result = task.getResult();
Collection<SelectorOptions<GetOperationOptions>> options = WebModelServiceUtils.createLookupTableRetrieveOptions();
final PrismObject<LookupTableType> lookupTable = WebModelServiceUtils.loadObject(LookupTableType.class,
lookupTableUid, options, pageBase, task, result);
if (lookupTable != null) {
panel = new AutoCompleteTextPanel<String>(id, new LookupPropertyModel<String>(valueWrapperModel, baseExpression,
lookupTable == null ? null : lookupTable.asObjectable()), type) {
@Override
public Iterator<String> getIterator(String input) {
return prepareAutoCompleteList(input, lookupTable).iterator();
}
@Override
public void checkInputValue(AutoCompleteTextField input, AjaxRequestTarget target, LookupPropertyModel model){
Iterator<String> lookupTableValuesIterator = prepareAutoCompleteList("", lookupTable).iterator();
String value = input.getInput();
boolean isValueExist = false;
if (value != null) {
if (value.trim().equals("")){
isValueExist = true;
} else {
while (lookupTableValuesIterator.hasNext()) {
String lookupTableValue = lookupTableValuesIterator.next();
if (value.trim().equals(lookupTableValue)) {
isValueExist = true;
break;
}
}
}
}
if (isValueExist){
input.setModelValue(new String[]{value});
target.add(PrismValuePanel.this.get(ID_FEEDBACK));
} else {
input.error("Entered value doesn't match any of available values and will not be saved.");
target.add(PrismValuePanel.this.get(ID_FEEDBACK));
}
}
};
} else {
panel = new TextPanel<>(id, new PropertyModel<String>(valueWrapperModel, baseExpression), type);
}
} else {
panel = new TextPanel<>(id, new PropertyModel<String>(valueWrapperModel, baseExpression), type);
}
}
} else if (item instanceof PrismReference) {
PrismContext prismContext = item.getPrismContext();
if (prismContext == null) {
prismContext = pageBase.getPrismContext();
}
QName targetTypeName = ((PrismReferenceDefinition) item.getDefinition()).getTargetTypeName();
Class targetClass = null;
if (targetTypeName != null && prismContext != null) {
targetClass = prismContext.getSchemaRegistry().determineCompileTimeClass(targetTypeName);
}
final Class typeClass = targetClass != null ? targetClass :
(item.getDefinition().getTypeClassIfKnown() != null ? item.getDefinition().getTypeClassIfKnown() : FocusType.class);
Collection typeClasses = new ArrayList();
// HACK HACK MID-3201 MID-3231
if (isUserOrgItem(item, typeClass)) {
typeClasses.add(UserType.class);
typeClasses.add(OrgType.class);
} else {
typeClasses.add(typeClass);
}
panel = new ValueChoosePanel(id,
new PropertyModel<>(valueWrapperModel, "value"), item.getValues(), false, typeClasses);
} else if (item instanceof PrismContainer<?>) {
AssociationWrapper itemWrapper = (AssociationWrapper) valueWrapperModel.getObject().getItem();
final PrismContainer container = (PrismContainer) item;
PrismContainerDefinition definition = container.getDefinition();
QName valueType = definition.getTypeName();
if (ShadowAssociationType.COMPLEX_TYPE.equals(valueType)) {
PrismContext prismContext = item.getPrismContext();
if (prismContext == null) {
prismContext = pageBase.getPrismContext();
}
ShadowType shadowType = ((ShadowType)itemWrapper.getContainer().getObject().getObject().asObjectable());
PrismObject<ResourceType> resource = shadowType.getResource().asPrismObject();
// HACK. The revive should not be here. Revive is no good. The next use of the resource will
// cause parsing of resource schema. We need some centralized place to maintain live cached copies
// of resources.
try {
resource.revive(prismContext);
} catch (SchemaException e) {
throw new SystemException(e.getMessage(), e);
}
RefinedResourceSchema refinedSchema;
CompositeRefinedObjectClassDefinition rOcDef;
try {
refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resource);
rOcDef = refinedSchema.determineCompositeObjectClassDefinition(shadowType.asPrismObject());
} catch (SchemaException e) {
throw new SystemException(e.getMessage(),e);
}
RefinedAssociationDefinition assocDef = itemWrapper.getRefinedAssociationDefinition();
RefinedObjectClassDefinition assocTargetDef = assocDef.getAssociationTarget();
ObjectQuery query = getAssociationsSearchQuery(prismContext, resource,
assocTargetDef.getTypeName(), assocTargetDef.getKind());
List values = item.getValues();
return new AssociationValueChoicePanel(id, valueWrapperModel, values, false, ShadowType.class,
query, assocTargetDef);
}
}
return panel;
}
private boolean isUserOrgItem(Item item, Class referenceType) {
return (referenceType == ObjectType.class || referenceType == FocusType.class) &&
(AbstractRoleType.F_APPROVER_REF.equals(item.getElementName()) ||
AbstractRoleType.F_OWNER_REF.equals(item.getElementName()));
}
private List<String> prepareAutoCompleteList(String input, PrismObject<LookupTableType> lookupTable){
List<String> values = new ArrayList<>();
if(lookupTable == null){
return values;
}
List<LookupTableRowType> rows = lookupTable.asObjectable().getRow();
if(input == null || input.isEmpty()){
for(LookupTableRowType row: rows){
values.add(WebComponentUtil.getOrigStringFromPoly(row.getLabel()));
}
} else {
for(LookupTableRowType row: rows){
if(WebComponentUtil.getOrigStringFromPoly(row.getLabel()) != null &&
WebComponentUtil.getOrigStringFromPoly(row.getLabel()).toLowerCase().contains(input.toLowerCase())){
values.add(WebComponentUtil.getOrigStringFromPoly(row.getLabel()));
}
}
}
return values;
}
private boolean isEnum(PrismProperty property){
PrismPropertyDefinition definition = property.getDefinition();
//// Object realValue = property.getAnyRealValue();
if (definition == null){
return property.getValueClass().isEnum();
}
//
// QName defName = definition.getName();
// Class clazz = definition.getPrismContext().getSchemaRegistry().determineCompileTimeClass(defName);
//
// return ((clazz != null && clazz.isEnum()) || ActivationType.F_ADMINISTRATIVE_STATUS.equals(definition.getName())
// || ActivationType.F_LOCKOUT_STATUS.equals(definition.getName()) || );
return (definition.getAllowedValues() != null && definition.getAllowedValues().size() > 0);
}
//TODO - try to get rid of <br> attributes when creating new lines in association attributes pop-up
private String createAssociationTooltipText(PrismProperty property){
StringBuilder sb = new StringBuilder();
sb.append(getString("prismValuePanel.message.association.attributes")).append("<br>");
if(property.getParent() != null && property.getParent().getParent() != null){
PrismObject<ShadowType> shadowPrism = (PrismObject<ShadowType>)property.getParent().getParent();
Collection<ResourceAttribute<?>> attributes = ShadowUtil.getAttributes(shadowPrism);
if (attributes == null || attributes.isEmpty()){
return sb.toString();
}
//TODO - this is a dirty fix for situation, when attribute value is too long and it is a string without white chars,
//thus it will not break in tooltip. break-all is also not good, since it can brake in the middle of words. What we
//are doing here is replacing every, with ,, (the same with @) is a zero-width space, so the attribute value
//will break after comma. This dirty fix will be removed when association editor is completed.
for (ResourceAttribute<?> attr : attributes){
for (Object realValue : attr.getRealValues()){
sb.append(getAttributeName(attr));
sb.append(":");
if (realValue != null) {
sb.append(realValue.toString().replace(",", ",").replace("@", "@").replace("_", "@"));
}
sb.append("<br>");
}
}
}
return sb.toString();
}
private String getAttributeName(ResourceAttribute<?> attr) {
if (attr.getDisplayName() != null){
return attr.getDisplayName();
}
if (attr.getNativeAttributeName() != null){
return attr.getNativeAttributeName();
}
if (attr.getElementName() != null){
return attr.getElementName().getLocalPart();
}
return null; //TODO: is this ok?? or better is exception or some default name??
}
private void addValue(AjaxRequestTarget target) {
Component inputPanel = this.get(ID_VALUE_CONTAINER).get(ID_INPUT);
ValueWrapper wrapper = valueWrapperModel.getObject();
ItemWrapper propertyWrapper = wrapper.getItem();
LOGGER.debug("Adding value of {}", propertyWrapper);
propertyWrapper.addValue();
ListView parent = findParent(ListView.class);
target.add(parent.getParent());
}
private void removeValue(AjaxRequestTarget target) {
ValueWrapper wrapper = valueWrapperModel.getObject();
ItemWrapper propertyWrapper = wrapper.getItem();
LOGGER.debug("Removing value of {}", propertyWrapper);
List<ValueWrapper> values = propertyWrapper.getValues();
Component inputPanel = this.get(ID_VALUE_CONTAINER).get(ID_INPUT);
switch (wrapper.getStatus()) {
case ADDED:
values.remove(wrapper);
break;
case DELETED:
error("Couldn't delete already deleted item: " + wrapper.toString());
target.add(((PageBase) getPage()).getFeedbackPanel());
case NOT_CHANGED:
if (inputPanel instanceof AssociationValueChoicePanel) {
((PropertyWrapper)propertyWrapper).setStatus(ValueStatus.DELETED);
}
wrapper.setStatus(ValueStatus.DELETED);
break;
}
// wrapper.getItem().getContainer().
int count = countUsableValues(propertyWrapper);
if (count == 0 && !hasEmptyPlaceholder(propertyWrapper)) {
if (inputPanel instanceof ValueChoosePanel) {
values.add(new ValueWrapper(propertyWrapper, new PrismReferenceValue(null), ValueStatus.ADDED));
} else if (inputPanel instanceof AssociationValueChoicePanel) {
Item item = propertyWrapper.getItem();
ItemPath path = item.getPath();
if (path != null){
}
// values.add(new ValueWrapper(propertyWrapper, new PrismPropertyValue(null), ValueStatus.ADDED));
} else {
values.add(new ValueWrapper(propertyWrapper, new PrismPropertyValue(null), ValueStatus.ADDED));
}
}
ListView parent = findParent(ListView.class);
target.add(parent.getParent());
}
private ObjectQuery getAssociationsSearchQuery(PrismContext prismContext, PrismObject resource, QName objectClass, ShadowKindType kind) {
return QueryBuilder.queryFor(ShadowType.class, prismContext)
.item(ShadowType.F_OBJECT_CLASS).eq(objectClass)
.and().item(ShadowType.F_KIND).eq(kind)
.and().item(ShadowType.F_RESOURCE_REF).ref(resource.getOid())
.build();
}
}