/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * 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 org.kie.workbench.common.stunner.bpmn.backend.indexing; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.jbpm.bpmn2.core.ItemDefinition; import org.jbpm.bpmn2.core.Message; import org.jbpm.compiler.xml.ProcessDataEventListener; import org.jbpm.process.core.context.variable.Variable; import org.jbpm.ruleflow.core.RuleFlowProcess; import org.jbpm.workflow.core.Node; import org.jbpm.workflow.core.node.RuleSetNode; import org.jbpm.workflow.core.node.SubProcessNode; import org.jbpm.workflow.core.node.WorkItemNode; import org.kie.api.definition.process.Process; import org.kie.workbench.common.services.refactoring.backend.server.impact.ResourceReferenceCollector; import org.kie.workbench.common.services.refactoring.model.index.Resource; import org.kie.workbench.common.services.refactoring.service.PartType; import org.kie.workbench.common.services.refactoring.service.ResourceType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This listener is called by the build process and immediately stores the indexing information (as it is also a * {@link ResourceReferenceCollector}) * </p> * In the {@link #onProcessAdded(Process)} method, it stores itself in the {@link Process}, allowing the {@link BpmnFileIndexer} * to retrieve the {@link BpmnProcessDataEventListener} instance later and add it to the indexing information. */ public class BpmnProcessDataEventListener extends ResourceReferenceCollector implements ProcessDataEventListener, Serializable { private static final Logger logger = LoggerFactory.getLogger(BpmnProcessDataEventListener.class); public static final String NAME = "BPMNProcessInfoCollector"; private List<Variable> variables = null; private Set<String> signals = new HashSet<>(); private Set<String> messages = new HashSet<>(); private Map<String, ItemDefinition> itemDefinitions = new HashMap<>(); private Process process; private Set<String> referencedClasses; private Set<String> unqualifiedClasses; // can be transient as it's only used when building private transient Resource resource; public Process getProcess() { return process; } // ProcessDataEventListener methods ------------------------------------------------------------------------------------------- @Override public void onNodeAdded(Node node) { if (node instanceof RuleSetNode) { RuleSetNode ruleSetNode = (RuleSetNode) node; String ruleFlowGroup = ruleSetNode.getRuleFlowGroup(); if (ruleFlowGroup != null) { addSharedReference(ruleFlowGroup, PartType.RULEFLOW_GROUP); } } else if (node instanceof WorkItemNode) { String taskName = ((WorkItemNode) node).getWork().getName(); addSharedReference(taskName, PartType.TASK_NAME); } else if (node instanceof SubProcessNode) { SubProcessNode subProcess = (SubProcessNode) node; String processName = subProcess.getProcessName(); if (!StringUtils.isEmpty(processName)) { addResourceReference(processName, ResourceType.BPMN2_NAME); } String processId = subProcess.getProcessId(); if (!StringUtils.isEmpty(processId)) { addResourceReference(processId, ResourceType.BPMN2); } } } @Override public void onProcessAdded(Process process) { logger.debug("Added process with id {} and name {}", process.getId(), process.getName()); this.process = process; resource = addResource(process.getId(), ResourceType.BPMN2); addResource(process.getName(), ResourceType.BPMN2_NAME); //add process descriptor as process meta data process.getMetaData().put(NAME, this); } @SuppressWarnings("unchecked") @Override public void onMetaDataAdded(String name, Object data) { if (name.equals("Variable")) { if (variables == null) { variables = new ArrayList<>(); } variables.add((Variable) data); } else if ("ItemDefinitions".equals(name)) { itemDefinitions = (Map<String, ItemDefinition>) data; } else if ("signalNames".equals(name)) { signals = (Set<String>) data; } else if ("Messages".equals(name)) { Map<String, Message> builderMessagesMap = (Map<String, Message>) data; messages = builderMessagesMap.keySet(); } } @Override public void onComplete(Process process) { // process item definitions visitItemDefinitions(); // process globals Map<String, String> globals = ((RuleFlowProcess) process).getGlobals(); visitGlobals(globals); // process imports Set<String> imports = ((RuleFlowProcess) process).getImports(); visitImports(imports); } private void visitItemDefinitions() { if (itemDefinitions != null) { for (ItemDefinition item : itemDefinitions.values()) { String structureRef = item.getStructureRef(); if (structureRef.contains(".")) { getReferencedClasses().add(structureRef); } else { getUnqualifiedClasses().add(structureRef); } } } } private void visitGlobals(Map<String, String> globals) { if (globals != null) { Set<String> globalNames = new HashSet<>(); for (Map.Entry<String, String> globalEntry : globals.entrySet()) { globalNames.add(globalEntry.getKey()); String type = globalEntry.getValue(); if (type.contains(".")) { getReferencedClasses().add(type); } else { getUnqualifiedClasses().add(type); } } for (String globalName : globalNames) { addSharedReference(globalName, PartType.GLOBAL); } } } private void visitImports(Set<String> imports) { if (imports != null) { for (String type : imports) { if (type.contains(".")) { getReferencedClasses().add(type); } else { getUnqualifiedClasses().add(type); } } } } @SuppressWarnings("unchecked") @Override public void onBuildComplete(Process process) { // process java dialect types Set<String> referencedTypes = (Set<String>) process.getMetaData().get("JavaDialectReferencedTypes"); if (referencedTypes != null && !referencedTypes.isEmpty()) { getReferencedClasses().addAll(referencedTypes); } Set<String> unqualifiedClasses = (Set<String>) process.getMetaData().get("JavaDialectUnqualifiedTypes"); if (unqualifiedClasses != null && !unqualifiedClasses.isEmpty()) { getUnqualifiedClasses().addAll(unqualifiedClasses); } // process java return value types referencedTypes = (Set<String>) process.getMetaData().get("JavaReturnValueReferencedTypes"); if (referencedTypes != null && !referencedTypes.isEmpty()) { getReferencedClasses().addAll(referencedTypes); } unqualifiedClasses = (Set<String>) process.getMetaData().get("JavaReturnValueUnqualifiedTypes"); if (unqualifiedClasses != null && !unqualifiedClasses.isEmpty()) { getUnqualifiedClasses().addAll(unqualifiedClasses); } // process mvel dialect types referencedTypes = (Set<String>) process.getMetaData().get("MVELDialectReferencedTypes"); if (referencedTypes != null && !referencedTypes.isEmpty()) { getReferencedClasses().addAll(referencedTypes); } // process mvel return value types referencedTypes = (Set<String>) process.getMetaData().get("MVELReturnValueReferencedTypes"); if (referencedTypes != null && !referencedTypes.isEmpty()) { getReferencedClasses().addAll(referencedTypes); } // process unqualified classes resolveUnqualifiedClasses(); // process variables if (variables != null) { for (Variable data : variables) { String type = data.getType().getStringType(); String itemSubjectRef = (String) data.getMetaData("ItemSubjectRef"); if (itemSubjectRef != null && itemDefinitions != null) { ItemDefinition itemDef = itemDefinitions.get(itemSubjectRef); type = itemDef.getStructureRef(); } resource.addPart(data.getName(), PartType.VARIABLE); if (type.contains(".")) { getReferencedClasses().add(type); } else { getUnqualifiedClasses().add(type); } } } // process signals, messages, etc. visitSignals(signals); visitSignals(messages); // (DRL) function imports visitFunctionImports(((RuleFlowProcess) process).getFunctionImports()); } private void visitFunctionImports(List<String> functionImports) { if (functionImports != null) { for (String functionImport : functionImports) { if (!functionImport.endsWith("*")) { addResourceReference(functionImport, ResourceType.FUNCTION); } } } } private void visitSignals(Collection<String> signals) { if (signals != null) { for (String signal : signals) { addSharedReference(signal, PartType.SIGNAL); } } } // Un/Qualified classes ------------------------------------------------------------------------------------------------------- private void resolveUnqualifiedClasses() { Set<String> qualifiedClassSimpleNames = new HashSet<String>(); for (String className : getReferencedClasses()) { qualifiedClassSimpleNames.add(className.substring(className.lastIndexOf('.') + 1)); } for (Iterator<String> iter = getUnqualifiedClasses().iterator(); iter.hasNext(); ) { if (qualifiedClassSimpleNames.contains(iter.next())) { iter.remove(); } } for (Iterator<String> iter = getUnqualifiedClasses().iterator(); iter.hasNext(); ) { String name = iter.next(); if ("Object".equals(name) || "String".equals(name) || "Float".equals(name) || "Integer".equals(name) || "Boolean".equals(name)) { getReferencedClasses().add("java.lang." + name); iter.remove(); } } for (String className : getUnqualifiedClasses()) { logger.warn("Unable to resolve unqualified class name, adding to list of classes: '{}'", className); getReferencedClasses().add(className); } } private Set<String> getReferencedClasses() { if (referencedClasses == null) { referencedClasses = new HashSet<>(4); } return referencedClasses; } private Set<String> getUnqualifiedClasses() { if (unqualifiedClasses == null) { unqualifiedClasses = new HashSet<>(4); } return unqualifiedClasses; } }