/******************************************************************************** | |
* Copyright (c) 2015-2021 Contributors to the Eclipse Foundation | |
* | |
* See the NOTICE file(s) distributed with this work for additional | |
* information regarding copyright ownership. | |
* | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v. 2.0 which is available at | |
* http://www.eclipse.org/legal/epl-2.0. | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
********************************************************************************/ | |
package org.eclipse.mdm.nodeprovider.entity; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.Optional; | |
import org.eclipse.mdm.api.base.adapter.Attribute; | |
import org.eclipse.mdm.api.base.model.Environment; | |
import org.eclipse.mdm.api.dflt.ApplicationContext; | |
import org.eclipse.mdm.businessobjects.utils.ServiceUtils; | |
import org.eclipse.mdm.nodeprovider.control.GenericNodeProvider; | |
import org.eclipse.mdm.protobuf.Mdm.Node; | |
import com.google.common.base.Strings; | |
/** | |
* A {@link NodeProviderRoot} defines the configuration of a | |
* {@link GenericNodeProvider}. It consists of an ID to uniquely identify the | |
* NodeProvider and a name that is displayed to the user. Furthermore it may | |
* contain {@link NodeLevel}s for each data source name defining the roots of | |
* the navigation trees. The special data source name | |
* {@link NodeProviderRoot#WILDCARD} is used for the fallback {@link NodeLevel} | |
* that is used if no other entry is present. | |
* | |
*/ | |
public class NodeProviderRoot { | |
public static final String WILDCARD = "*"; | |
private String id; | |
private String name; | |
private Map<String, NodeLevel> contexts = new HashMap<>(); | |
/** | |
* Default constructor | |
*/ | |
public NodeProviderRoot() { | |
} | |
/** | |
* @return the id | |
*/ | |
public String getId() { | |
return id; | |
} | |
/** | |
* @param id the id to set | |
*/ | |
public void setId(String id) { | |
this.id = id; | |
} | |
/** | |
* @return the name | |
*/ | |
public String getName() { | |
return name; | |
} | |
/** | |
* @param name the name to set | |
*/ | |
public void setName(String name) { | |
this.name = name; | |
} | |
/** | |
* @return the contexts | |
*/ | |
public Map<String, NodeLevel> getContexts() { | |
return contexts; | |
} | |
/** | |
* @param contexts the contexts to set | |
*/ | |
public void setContexts(Map<String, NodeLevel> contexts) { | |
this.contexts = contexts; | |
} | |
/** | |
* Get the {@link NodeLevel} for the given sourceName. If the given sourceName | |
* has no entry, the {@link NodeLevel} for the {@link NodeProviderRoot#WILDCARD} | |
* is returned. | |
* | |
* @param sourceName | |
* @return NodeLevel for the given sourceName | |
*/ | |
public NodeLevel getNodeLevel(String sourceName) { | |
NodeLevel nl = contexts.get(sourceName); | |
if (nl == null) { | |
// if the NodeProviderRoot was constructed via NodeLevelUtil contexts already | |
// contains a NodeLevel for each source name. If still nothing is found we check | |
// the wildcard again just to be sure. | |
nl = contexts.get(WILDCARD); | |
} | |
return nl; | |
} | |
/** | |
* Find the parent NodeLevel for a given nodeLevel. | |
* | |
* @param context | |
* @param nodeLevel | |
* @return parent NodeLevel | |
*/ | |
public NodeLevel getParentNodeLevel(ApplicationContext context, NodeLevel nodeLevel) { | |
NodeLevel n = getNodeLevel(context.getSourceName()); | |
if (n.equals(nodeLevel)) { | |
return null; | |
} | |
while (n.getChild() != null && !nodeLevel.equals(n.getChild())) { | |
n = n.getChild(); | |
} | |
return n; | |
} | |
/** | |
* Find the child NodeLevel for a given nodeLevel. | |
* | |
* @param context | |
* @param nodeLevel | |
* @return child NodeLevel | |
*/ | |
public Optional<NodeLevel> getChildNodeLevel(ApplicationContext context, Node nodeLevel) { | |
NodeLevel childNodeLevel = null; | |
if (nodeLevel == null) { | |
childNodeLevel = getNodeLevel(context, Environment.class.getSimpleName(), null); | |
} else { | |
childNodeLevel = getNodeLevel(context, nodeLevel.getType(), nodeLevel.getIdAttribute()); | |
if (childNodeLevel == null) { | |
return Optional.empty(); | |
} | |
childNodeLevel = childNodeLevel.getChild(); | |
} | |
return Optional.ofNullable(childNodeLevel); | |
} | |
/** | |
* @param context | |
* @param type | |
* @param filterAttribute | |
* @return NodeLevel for given type and idAttribute | |
*/ | |
public NodeLevel getNodeLevel(ApplicationContext context, String type, String filterAttribute) { | |
NodeLevel n = getNodeLevel(context.getSourceName()); | |
while (n != null && !nodeLevelMatches(n, type, filterAttribute)) { | |
n = n.getChild(); | |
} | |
return n; | |
} | |
/** | |
* @param nodeLevel | |
* @param type | |
* @param filterAttribute | |
* @return true, if the given type and idAttribute matches the nodeLevel | |
*/ | |
/** | |
* TODO 05.02.2021, jst: Match should respect labelAttributes / value precision | |
* to work properly for nested virtual nodes of same type with different value | |
* precision. | |
* | |
* Current fails in scenarios like: | |
* | |
* (...) | |---Test.DateCreated - YEAR | |---Test.DateCreated - MONTH | |--- | |
* Test.DateCreated - DAY | |
*/ | |
private boolean nodeLevelMatches(NodeLevel nodeLevel, String type, String filterAttribute) { | |
String first = nodeLevel.getFilterAttributes().stream().findFirst().map(Attribute::getName).orElse(null); | |
return ServiceUtils.workaroundForTypeMapping(type) | |
.equals(ServiceUtils.workaroundForTypeMapping(nodeLevel.getEntityType())) | |
&& (Strings.isNullOrEmpty(filterAttribute) || filterAttribute.equals(first)); | |
} | |
} |