/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are 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: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.core.utils; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.eclipse.jubula.client.core.businessprocess.TestCaseParamBP; import org.eclipse.jubula.client.core.i18n.Messages; import org.eclipse.jubula.client.core.model.INodePO; import org.eclipse.jubula.client.core.model.IParamDescriptionPO; import org.eclipse.jubula.client.core.model.IParamNodePO; import org.eclipse.jubula.client.core.model.IParameterInterfacePO; import org.eclipse.jubula.client.core.model.ISpecTestCasePO; import org.eclipse.jubula.client.core.model.ITestDataCubePO; import org.eclipse.jubula.client.core.model.ITestSuitePO; import org.eclipse.jubula.client.core.persistence.NodePM; import org.eclipse.jubula.client.core.utils.ParamValueConverter.ConvValidationState; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.exception.Assert; import org.eclipse.jubula.tools.internal.exception.InvalidDataException; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.osgi.util.NLS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class to convert and validate a reference token. The conversion is carried * out between GUI and model representation and vice versa. The validation * refers to semantical correctness of the GUI representation of reference * string. * * @author BREDEX GmbH * @created 14.08.2007 */ public class RefToken extends AbstractParamValueToken { /** the logger */ private static Logger log = LoggerFactory.getLogger(RefToken.class); /** prefix for a reference */ private static final String PREFIX = "={"; //$NON-NLS-1$ /** * <code>m_guiString</code> string represents the token in the GUI */ private String m_guiString = null; /** * <code>m_modelString</code>string represents the token in the model<br> * e.g. <b>"=REF"</b> or <b>"={REF}"</b> --> GUI representation <br> * <b>"=GUID"</b> or <b>={GUID}</b> --> model representation */ private String m_modelString = null; /** flag for differentiation between token creation based on string in GUI- or * model representation */ private boolean m_isTokenGuiBased; /** node holding this reference - can be null, but then the RefToken is invalid */ private IParameterInterfacePO m_currentNode; /** * use this constructor only for references coming from GUI * @param string represents the token * @param isGuiString flag for differentiation between GUI- and model representation of string * @param startPos index of first character of token in entire string * @param node holding this reference * @param desc param description belonging to this reference */ public RefToken(String string, boolean isGuiString, int startPos, IParameterInterfacePO node, IParamDescriptionPO desc) { super(string, startPos, desc); if (!isValid(string, isGuiString)) { throw new IllegalArgumentException(NLS.bind( Messages.SyntaxErrorInReference, string)); } m_isTokenGuiBased = isGuiString; if (isGuiString) { m_guiString = string; } else { m_modelString = string; } m_currentNode = node; } /** * creates the model representation from the GUI representation * @return modelString */ public String getModelString() { if (m_modelString == null && m_guiString != null) { String uuid = computeUUID(); if (uuid != null) { m_modelString = replaceCore(computeUUID(), m_guiString); } } return m_modelString; } /** * hint: returned UUID is null if reference is new and the associated parameter * in parent node is not yet created * @return GUID belonging to this reference */ private String computeUUID() { String uuid = StringConstants.EMPTY; if (m_modelString != null) { uuid = extractCore(m_modelString); } else if (m_guiString != null) { if (m_currentNode instanceof INodePO) { INodePO parent = NodePM.getSpecTestCaseParent( (INodePO)m_currentNode); String refName = extractCore(m_guiString); if (parent instanceof IParamNodePO) { IParamNodePO parentNode = (IParamNodePO)parent; IParamDescriptionPO desc = parentNode.getParameterForName(refName); if (desc != null) { uuid = desc.getUniqueId(); } else { return null; } } else { StringBuilder msg = new StringBuilder(); msg.append(Messages.Node); msg.append(StringConstants.SPACE); msg.append(m_currentNode.getName()); msg.append(StringConstants.SPACE); msg.append(Messages.WithReferenceIsNotChildOfParamNode); Assert.notReached(msg.toString()); } } } return uuid; } /** * @param repl replacement (UUID or reference name) * @param str base string * @return replaced string */ public static String replaceCore(String repl, String str) { int start = -1; int end = -1; StringBuilder builder = new StringBuilder(str); if (str.startsWith(PREFIX)) { start = 2; end = str.length() - 1; } else { start = 1; end = str.length(); } if (start < end) { builder.replace(start, end, repl); return builder.toString(); } Assert.notReached(Messages.UnexpectedProblemWithStringReplacement); return str; } /** * @param s string for syntax validation * @param isGuiString flag to distinct GUI- and modelStrings * @return if the syntax of guiString is correct */ private boolean isValid(String s, boolean isGuiString) { if (!isGuiString) { String string = extractCore(s); final String wordRegex = "[0-9a-fA-F]{32}"; //$NON-NLS-1$ return (Pattern.matches(wordRegex, string)); } return true; } /** * @param s string in GUI- or model representation * @return reference name respectively UUID-portion of entire string */ public static String extractCore(String s) { StringBuilder builder = new StringBuilder(s); if (s != null && s.length() != 0) { if (s.startsWith("={") && s.endsWith("}")) { //$NON-NLS-1$ //$NON-NLS-2$ builder.delete(0, 2); builder.deleteCharAt(builder.length() - 1); } else if (s.startsWith("=")) { //$NON-NLS-1$ builder.deleteCharAt(0); } } return builder.toString(); } /** * validates, if the reference name and the associated type is allowed and * the interface may be modified * {@inheritDoc} * @see IParamValueToken#validate(INodePO) */ public ConvValidationState validate() { if (m_currentNode == null) { setErrorKey(MessageIDs.E_NO_REF_ALLOWED); return ConvValidationState.invalid; } ConvValidationState state = ConvValidationState.notSet; if (m_currentNode instanceof ISpecTestCasePO) { setErrorKey(MessageIDs.E_NO_REF_FOR_SPEC_TC); return ConvValidationState.invalid; } else if (m_currentNode instanceof INodePO && ((INodePO)m_currentNode).getSpecAncestor() instanceof ITestSuitePO) { setErrorKey(MessageIDs.E_REF_IN_TS); return ConvValidationState.invalid; } else if (m_currentNode instanceof ITestDataCubePO) { setErrorKey(MessageIDs.E_REF_IN_TDC); return ConvValidationState.invalid; } final boolean isModifiable = TestCaseParamBP.isReferenceValueAllowed( m_currentNode); if (m_isTokenGuiBased) { INodePO parent = NodePM.getSpecTestCaseParent( (INodePO)m_currentNode); String refName = extractCore(m_guiString); if (parent instanceof ISpecTestCasePO) { ISpecTestCasePO specTc = (ISpecTestCasePO)parent; List<IParamDescriptionPO> descs = specTc.getParameterList(); Map<String, IParamDescriptionPO> paramNames = new HashMap<String, IParamDescriptionPO>(); for (IParamDescriptionPO desc : descs) { paramNames.put(desc.getName(), desc); } if ((paramNames.keySet()).contains(refName)) { IParamDescriptionPO desc = paramNames.get(refName); String pType = getParamDescription().getType(); // We allow conversion from any type to String if ("java.lang.String".equals(pType) //$NON-NLS-1$ || desc.getType().equals(pType)) { state = ConvValidationState.valid; } else { state = ConvValidationState.invalid; setErrorKey(MessageIDs.E_INVALID_REF_TYPE); } } else { if (isModifiable) { state = ConvValidationState.valid; } else { state = ConvValidationState.invalid; setErrorKey(MessageIDs.E_INVALID_REF); for (String paramName : paramNames.keySet()) { if (paramName.startsWith(refName)) { IParamDescriptionPO desc = paramNames.get(paramName); if (desc.getType().equals( getParamDescription().getType())) { state = ConvValidationState.undecided; break; } } } } } } else { throw new UnsupportedOperationException( Messages.NotAllowedToAddReferenceToNodeASpecTestCase); } } else { // assumption, that semantic of modelString is correct state = ConvValidationState.valid; } return state; } /** * gets the real value for a reference * @param stack current execution stack * @return the real value for this reference token and given dataset number * @throws InvalidDataException if given reference is not resolvable */ public String getExecutionString(List<ExecObject> stack) throws InvalidDataException { String refGuid = extractCore(getModelString()); ListIterator <ExecObject> it = stack.listIterator(stack.size()); while (it.hasPrevious()) { ExecObject obj = it.previous(); String parameterValue = obj.getParameterValue(refGuid); if (parameterValue != null) { return parameterValue; } } throwInvalidDataException(extractCore(getGuiString())); return null; } /** * throws an exception, if neither a value or a further reference is * available for given reference * * @param reference * reference, which isn't resolvable * @throws InvalidDataException * in case of missing testdata for given reference */ private void throwInvalidDataException(String reference) throws InvalidDataException { throw new InvalidDataException(Messages.Reference + reference + StringConstants.SPACE + Messages.NotResolvable, MessageIDs.E_NO_REFERENCE); } /** * {@inheritDoc} * @see org.eclipse.jubula.client.core.utils.IParamValueToken#getValue() */ public String getGuiString() { if (m_modelString != null && m_guiString == null) { m_guiString = replaceCore(computeReferenceName(), m_modelString); } return m_guiString; } /** * compute reference name based on GUI- or model string * @return reference name */ private String computeReferenceName() { String refName = StringConstants.EMPTY; if (m_guiString != null) { refName = extractCore(m_guiString); } else if (m_modelString != null) { String uuid = extractCore(m_modelString); INodePO parent = NodePM.getSpecTestCaseParent( (INodePO)m_currentNode); if (parent instanceof IParamNodePO) { IParamNodePO parentNode = (IParamNodePO) parent; IParamDescriptionPO desc = parentNode .getParameterForUniqueId(uuid); if (desc != null) { refName = desc.getName(); } else { String id = (uuid != null) ? uuid : StringConstants.EMPTY; refName = id; log.error(NLS.bind(Messages.InvalidUuidInReference, id)); } } else { Assert.notReached( Messages.NodeWithReferenceIsNotChildOfParamNode); } } return refName; } /** * @param modelString The modelString to set. */ void setModelString(String modelString) { m_modelString = modelString; } /** * * {@inheritDoc} */ public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } RefToken otherObj = (RefToken)obj; return new EqualsBuilder() .append(getModelString(), otherObj.getModelString()) .isEquals(); } /** * * {@inheritDoc} */ public int hashCode() { return new HashCodeBuilder().append(getModelString()).toHashCode(); } }