/******************************************************************************* * Copyright (c) 2015 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.batch.internal.core.validation; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; import org.jboss.tools.batch.core.BatchConstants; import org.jboss.tools.batch.internal.core.preferences.BatchSeverityPreferences; import org.jboss.tools.common.xml.XMLUtilities; import org.w3c.dom.Attr; import org.w3c.dom.Element; /** * * @author Viacheslav Kabanovich * */ public class TransitionsValidator implements BatchConstants { JobTransitionsValidator jobTransitions; BatchValidator validator; Map<String, FlowNode> flowElements = new HashMap<String, FlowNode>(); public TransitionsValidator(BatchValidator validator, JobTransitionsValidator jobLevel) { this.validator = validator; this.jobTransitions = jobLevel; } public String addFlowElement(Element element) { String id = element.getAttribute(ATTR_ID).trim(); if(id.length() > 0) { FlowNode n = new FlowNode(element); if(!flowElements.containsKey(id)) { flowElements.put(id, n); } } return id; } public void validate(IFile file) { for (FlowNode n: flowElements.values()) { String fromNodeId = n.getIDValue(); validateTransitionAttribute(n.element, fromNodeId, ATTR_NEXT, file); for (Element nextElement: XMLUtilities.getChildren(n.element, TAG_NEXT)) { validateTransitionAttribute(nextElement, fromNodeId, ATTR_TO, file); } for (Element nextElement: XMLUtilities.getChildren(n.element, TAG_STOP)) { validateRestartAttribute(nextElement, fromNodeId, file); } } while(reduce() > 0) {} for (FlowNode n: flowElements.values()) { String fromNodeId = n.getIDValue(); for (FlowLink link: n.out) { String toNodeId = link.toNodeId; SimpleReference ref = new SimpleReference(link.attr.getOwnerElement(), link.attr.getName(), file); validator.addProblem(BatchValidationMessages.LOOP_IS_DETECTED, BatchSeverityPreferences.LOOP_IS_DETECTED, new String[]{fromNodeId, toNodeId}, ref.getLength(), ref.getStartPosition(), file/*, quickFixId*/); } } } void validateTransitionAttribute(Element fromElement, String fromNodeId, String attrName, IFile file) { Attr next = fromElement.getAttributeNode(attrName); if(next != null) { String toNodeId = fromElement.getAttribute(attrName).trim(); if(toNodeId.length() > 0) { if(flowElements.get(toNodeId) == null) { validator.addProblem(BatchValidationMessages.TARGET_NOT_FOUND, BatchSeverityPreferences.TARGET_NOT_FOUND, fromElement, attrName, file, -1); } else if(toNodeId.equals(fromNodeId)) { validator.addProblem(BatchValidationMessages.TRANSITION_TO_SELF, BatchSeverityPreferences.LOOP_IS_DETECTED, fromElement, attrName, file, -1); } else { FlowLink link = new FlowLink(fromNodeId, next, toNodeId); flowElements.get(fromNodeId).out.add(link); flowElements.get(toNodeId).in.add(link); } } } } void validateRestartAttribute(Element fromElement, String fromNodeId, IFile file) { Attr next = fromElement.getAttributeNode(ATTR_RESTART); if(next != null) { String toNodeId = fromElement.getAttribute(ATTR_RESTART).trim(); if(toNodeId.length() > 0) { if(!jobTransitions.ids.contains(toNodeId)) { validator.addProblem(BatchValidationMessages.TARGET_NOT_FOUND_ON_JOB_LEVEL, BatchSeverityPreferences.TARGET_NOT_FOUND, fromElement, ATTR_RESTART, file, -1); } } } } private int reduce() { String[] ids = flowElements.keySet().toArray(new String[0]); int removedLinks = 0; for (String id: ids) { FlowNode n = flowElements.get(id); if(n.in.isEmpty()) { for (FlowLink link: n.out) { FlowNode to = flowElements.get(link.toNodeId); if (to != null) { if(to.in.remove(link)) removedLinks++; } } flowElements.remove(id); } else if(n.out.isEmpty()) { for (FlowLink link: n.in) { FlowNode from = flowElements.get(link.fromNodeId); if (from != null) { if(from.out.remove(link)) removedLinks++; } } flowElements.remove(id); } } return removedLinks; } static class FlowNode { Element element; Attr id; Set<FlowLink> in = new HashSet<FlowLink>(); Set<FlowLink> out = new HashSet<FlowLink>(); public FlowNode(Element element) { this.element = element; id = element.getAttributeNode(ATTR_ID); } public String getIDValue() { return element.getAttribute(ATTR_ID).trim(); } } static class FlowLink { String fromNodeId; String toNodeId; Attr attr; public FlowLink(String fromNodeId, Attr attr, String toNodeId) { this.fromNodeId = fromNodeId; this.toNodeId = toNodeId; this.attr = attr; } } } class JobTransitionsValidator extends TransitionsValidator { Set<String> ids = new HashSet<String>(); public JobTransitionsValidator(BatchValidator validator) { super(validator, null); this.jobTransitions = this; } public String addFlowElement(Element element) { String id = super.addFlowElement(element); if(id != null && id.length() > 0) { ids.add(id); } return id; } }