Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 io.uhndata.cards.formcompletionstatus;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.BiPredicate;
import java.util.function.Function;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ConditionalSectionUtils
{
private static final Logger LOGGER = LoggerFactory.getLogger(ConditionalSectionUtils.class);
private static final String PROP_QUESTION = "question";
private static final String PROP_IS_REFERENCE = "isReference";
private static final String PROP_TYPE = "dataType";
private static final String PROP_VALUE = "value";
private static final String PROP_REQUIRE_ALL = "requireAll";
private static final BiPredicate SINGLE_LISTS =
(a, b) -> (a.getValues().size() == 1 && b.getValues().size() == 1);
/**
* Hide the utility class constructor.
*/
private ConditionalSectionUtils()
{
}
/**
* Gets the questionnaire section node referenced by the AnswerSection NodeBuilder nb.
*
* @param resourceSession the current session
* @param answerSection the answer section whose section should be retrieved
* @return the section Node object referenced by answerSection
*/
private static Node getSectionNode(final Session resourceSession, final NodeBuilder answerSection)
{
try {
if (answerSection.hasProperty("section")) {
final String sectionNodeReference = answerSection.getProperty("section").getValue(Type.REFERENCE);
return resourceSession.getNodeByIdentifier(sectionNodeReference);
}
} catch (final RepositoryException ex) {
return null;
}
return null;
}
/**
* Returns the first NodeBuilder with a question value equal to questionUUID that is a descendant of the parent
* node. If no such node can be found, null is returned.
*
* @param parent the NodeBuilder to search through its children
* @param questionUUID the UUID String for which the child's question property must be equal to
* @return the first NodeBuilder with a question value equal to questionUUID that is a descendant of the parent
* NodeBuilder, or null if such a node does not exist.
*/
private static NodeBuilder getAnswerForQuestion(final NodeBuilder parent, final String questionUUID)
{
final Iterable childrenNames = parent.getChildNodeNames();
for (final String childName : childrenNames) {
final NodeBuilder child = parent.getChildNode(childName);
// Check if this is the answer we're looking for
if (child.hasProperty(PROP_QUESTION)
&& questionUUID.equals(child.getProperty(PROP_QUESTION).getValue(Type.STRING))) {
return child;
}
// If this is an answer section, look for the answer in it, and return if found
if ("cards:AnswerSection".equals(child.getName("jcr:primaryType"))) {
NodeBuilder sectionResult = getAnswerForQuestion(child, questionUUID);
if (sectionResult != null) {
return sectionResult;
}
}
}
return null;
}
/*
* Read in a string, inStr, and return it with any non-allowed chars removed.
*/
private static String sanitizeNodeName(final String inStr)
{
final String inStrLower = inStr.toLowerCase();
String outStr = "";
for (int i = 0; i < inStr.length(); i++) {
if ("abcdefghijklmnopqrstuvwxyz 0123456789_-".indexOf(inStrLower.charAt(i)) > -1) {
outStr += inStr.charAt(i);
}
}
return outStr;
}
private static PropertyState getPropertyStateFromRef(final Node operand,
final Node sectionNode, final NodeBuilder form) throws RepositoryException
{
String key = sanitizeNodeName(operand.getProperty(PROP_VALUE).getValues()[0].getString());
final Node questionnaire = getQuestionnaireForSection(sectionNode);
final Node question = getQuestionWithName(questionnaire, key);
if (question == null) {
return null;
}
final String questionUUID = question.getIdentifier();
// Get the node from the Form containing the answer to keyNode
final NodeBuilder answer = getAnswerForQuestion(form, questionUUID);
if (answer == null) {
return null;
}
return answer.getProperty(PROP_VALUE);
}
/**
* Retrieves for the Questionnaire that a Section belongs to. This is usually the parent node, but in the case of a
* nested section, it may be higher up the ancestors chain.
*
* @param section the Section whose Questionnaire to retrieve
* @return a Questionnaire node, or {@code null} if navigating the tree fails
*/
private static Node getQuestionnaireForSection(final Node section)
{
Node result = section;
try {
while (section != null && !"cards:Questionnaire".equals(result.getPrimaryNodeType().getName())) {
result = result.getParent();
}
} catch (RepositoryException e) {
LOGGER.warn("Unexpected error looking up the questionnaire: {}", e.getMessage(), e);
return null;
}
return result;
}
/**
* Retrieves the Question node with the given name. This is needed to get from a simple question name, like
* {@code "gender"}, to the JCR UUID used in the actual reference from the answer to the question.
*
* @param parent the node to (recursively) look in, starting with the Questionnaire
* @param questionName the simple question name
* @return the Question node, or {@code null} if the question cannot be found
*/
private static Node getQuestionWithName(final Node parent, final String questionName)
{
try {
final NodeIterator children = parent.getNodes();
while (children.hasNext()) {
final Node childNode = children.nextNode();
if (questionName.equals(childNode.getName())) {
return childNode;
}
if ("cards:Section".equals(childNode.getPrimaryNodeType().getName())) {
Node sectionResult = getQuestionWithName(childNode, questionName);
if (sectionResult != null) {
return sectionResult;
}
}
}
} catch (RepositoryException e) {
LOGGER.warn("Failed to look up question: {}", e.getMessage(), e);
}
return null;
}
public static boolean isConditionSatisfied(final Session resourceSession,
final NodeBuilder answerSection, final NodeBuilder form) throws RepositoryException
{
final Node sectionNode = getSectionNode(resourceSession, answerSection);
if (sectionNode != null) {
final Conditional conditional = Conditional.findConditional(sectionNode, form);
if (conditional != null) {
return conditional.isSatisfied();
}
}
return true;
}
/**
* The data type of the operand values, as indicated in the {@code dataType} property of the
* {@code cards:Conditional} node.
*/
@SuppressWarnings("unchecked")
private enum OperandType
{
TEXT(Type.STRINGS, v -> {
try {
return (Comparable