package de.persosim.simulator.apdumatching; import de.persosim.simulator.tlv.TlvDataObject; import de.persosim.simulator.tlv.TlvDataObjectContainer; import de.persosim.simulator.tlv.TlvPath; import de.persosim.simulator.tlv.TlvTag; import de.persosim.simulator.tlv.TlvValue; /** * This class represents a specification of requirements concerning TLV data * objects {@link TlvDataObject}. There is no explicit distinction between * primitive or constructed TLV data objects. Distinction is implicit by (not) * providing specifications for sub elements. Any specification must contain a * {@link TlvTag} and additionally may explicitly specify whether it is expected * to (not) match a TLV data object or matching is optional (default is match). * The constants for matching requirements * {@link ApduSpecificationConstants#REQ_MATCH}, * {@link ApduSpecificationConstants#REQ_MISMATCH} and * {@link ApduSpecificationConstants#REQ_OPTIONAL} are specified in the * interface {@link ApduSpecificationConstants}. * * Constructed TLV data objects may be specified by adding requirements * concerning contained sub elements. Sub elements may be specified the same way * recursively. Additionally it can be specified whether sub element * specifications that are not explicitly stated this way are allowed. Sub * elements internally are stored in an object of type * {@link TlvSpecificationContainer} so many methods, e.g. * {@link #add(TlvSpecification)} will be delegated to this object. * * @author slutters * */ public class TlvSpecification implements ApduSpecificationConstants { protected TlvTag tlvTag; protected byte required; protected TlvSpecificationContainer subTags; /** * This constructor constructs a {@link TlvSpecification} based on a tag, permission for unknown sub tags and matching requirements. * @param tlvTag the TLV tag * @param allowUnspecifiedSubTags whether unspecified sub tags are accepted * @param isStrictOrder specifies whether the order of sub tags is to be evaluated as expected in strict given order * @param required matching requirements (see class documentation) */ public TlvSpecification(TlvTag tlvTag, boolean allowUnspecifiedSubTags, boolean isStrictOrder, byte required) { this.tlvTag = tlvTag; this.required = required; this.subTags = new TlvSpecificationContainer(allowUnspecifiedSubTags, isStrictOrder); } /** * This constructor constructs a {@link TlvSpecification} based on a tag. * Default settings are: * unknown sub tags allowed: false * matching requirements : required * @param tlvTag the TLV tag * @param required matching requirements (see class documentation) */ public TlvSpecification(TlvTag tlvTag, byte required) { this(tlvTag, DO_NOT_ALLOW_FURTHER_TAGS, STRICT_ORDER, required); } /** * This constructor constructs a {@link TlvSpecification} based on a tag. * Default settings are: * unknown sub tags allowed: false * matching requirements : required * @param tlvTag the TLV tag */ public TlvSpecification(TlvTag tlvTag) { this(tlvTag, DO_NOT_ALLOW_FURTHER_TAGS, STRICT_ORDER, REQ_MATCH); } /** * @see {@link TlvSpecificationContainer#add(TlvSpecification)} */ public void add(TlvSpecification tlvSpec) { subTags.add(tlvSpec); } /** * @see {@link TlvSpecificationContainer#add(TlvPath, int, TlvSpecification)} */ public void add(TlvPath path, int pathOffset, TlvSpecification tlvSpec) { subTags.add(path, pathOffset, tlvSpec); } /** * @see {@link TlvSpecificationContainer#setAllowUnspecifiedSubTags(boolean)} */ public void setAllowUnspecifiedSubTags(boolean allowUnspecifiedSubTags) { subTags.setAllowUnspecifiedSubTags(allowUnspecifiedSubTags); } /** * @see {@link TlvSpecificationContainer#setStrictOrder(boolean)} */ public void setStrictOrder(boolean strictOrder) { subTags.setStrictOrder(strictOrder); } /** * @see {@link TlvSpecificationContainer#isEmpty()} */ public boolean isEmpty() { return subTags.isEmpty(); } /** * This method performs a matching against the provided {@link TlvDataObject}. * Matching is performed based on the {@link TlvTag} and the sub elements recursively. * @param tlvDataObject the {@link TlvDataObject} to match against * @return whether this object matches the provided {@link TlvDataObject} */ public boolean matches(TlvDataObject tlvDataObject) { if(!matches(tlvDataObject.getTlvTag())) {return false;} if(this.required == REQ_MISMATCH) {return false;} TlvValue value = tlvDataObject.getTlvValue(); // IMPL support for primitive TLV data objects containing further TLV data objects disguised as Octet Strings if(value instanceof TlvDataObjectContainer) { return subTags.matches((TlvDataObjectContainer) value); } else{ if(subTags.size() > 0) { return false; } else{ return true; } } } /** * This method returns whether the provided {@link TlvTag} matches the tag specified within this object. * @param anotherTlvTag the {@link TlvTag} to match against * @return whether the provided {@link TlvTag} matches the tag specified within this object */ public boolean matches(TlvTag anotherTlvTag) { return tlvTag.matches(anotherTlvTag); } /** * @return the subTags */ public TlvSpecificationContainer getSubTags() { return subTags; } public byte getRequired() { return required; } public void setRequired(byte required) { this.required = required; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + required; result = prime * result + ((subTags == null) ? 0 : subTags.hashCode()); result = prime * result + ((tlvTag == null) ? 0 : tlvTag.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TlvSpecification other = (TlvSpecification) obj; if (required != other.required) return false; if (subTags == null) { if (other.subTags != null) return false; } else if (!subTags.equals(other.subTags)) return false; if (tlvTag == null) { if (other.tlvTag != null) return false; } else if (!tlvTag.equals(other.tlvTag)) return false; return true; } }