/*
* Copyright (C) 2007 ETH Zurich
*
* This file is part of Fosstrak (www.fosstrak.org).
*
* Fosstrak is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* Fosstrak is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Fosstrak; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package org.fosstrak.ale.server;
import java.util.ArrayList;
import java.util.List;
import org.fosstrak.ale.exception.ECSpecValidationException;
import org.fosstrak.ale.exception.ImplementationException;
/**
* This class represents an tag, filter or group pattern.
*
* @author regli
*/
public class Pattern {
/** content of the first field */
private static final String FIRST_FIELD = "urn";
/** content of the second field */
private static final String SECOND_FIELD = "epc";
/** possible contents of the third field */
private static final String[] THIRD_FIELDS = new String[] {"urn", "tag", "pat", "id", "idpat", "raw"};
/** how this pattern is used (tag, filter or group) */
private final PatternUsage usage;
/** conent of third field */
private final String thirdField;
/** type of this pattern */
private final PatternType type;
/** data fields of this pattern */
private final List<PatternDataField> dataFields = new ArrayList<PatternDataField>();
/**
* Contructor sets the usage and parses the pattern.
*
* @param pattern to parse
* @param usage of this pattern
* @throws ECSpecValidationException if the pattern is invalid
*/
public Pattern(String pattern, PatternUsage usage) throws ECSpecValidationException {
// set fields
this.usage = usage;
StringBuffer thirdFieldString = new StringBuffer();
thirdFieldString.append("(");
for (int i = 0; i < THIRD_FIELDS.length; i++) {
if (i > 0) thirdFieldString.append(" | ");
thirdFieldString.append(THIRD_FIELDS[i]);
}
thirdFieldString.append(")");
pattern = pattern.replaceAll("\\s", "");
// split pattern and check first fields
String[] parts = pattern.split(":");
if (parts.length != 5) {
throw new ECSpecValidationException("Invalid Pattern '" + pattern + "'." +
" Pattern must have the form '" + FIRST_FIELD + ":" + SECOND_FIELD + ":" + thirdFieldString + ":tag-type:data-fields'.");
} else {
thirdField = parts[2];
if (!FIRST_FIELD.equals(parts[0]) || !SECOND_FIELD.equals(parts[1]) || !containsString(THIRD_FIELDS, thirdField)) {
throw new ECSpecValidationException("Invalid Pattern '" + pattern + "'." +
" Pattern must start with '" + FIRST_FIELD + ":" + SECOND_FIELD + ":" + thirdFieldString + "'.");
} else {
// get pattern type
type = PatternType.getType(parts[3]);
// parse data fields
parseDataFields(parts[4], pattern);
}
}
String[] epc = parts[4].split("[.]");
if (pattern.contains("*")) {
if (epc[0].equals("*") && epc[1].equals("*") && epc[2].equals("*"))
{
}
else if (epc[1].equals("*") && epc[2].equals("*"))
{
}
else if (epc[2].equals("*"))
{
}
else if (epc[0].equals("X") && epc[1].equals("X") && epc[2].equals("*"))
{
}
else if (epc[0].equals("*") && epc[1].equals("X") && epc[2].equals("*"))
{
}
else if (epc[0].equals("X") && epc[1].equals("*") && epc[2].equals("*"))
{
}
else {
throw new ECSpecValidationException("Invalid Pattern '" + pattern + "'." +
" Pattern invalid by wrong * sets.");
}
}
}
/**
* This method returns the type of this pattern.
*
* @return type of pattern
*/
public PatternType getType() {
return type;
}
/**
* This method returns the filter of this pattern.
*
* @return filter of pattern
*/
public PatternDataField getFilter() {
return dataFields.get(0);
}
/**
* This method returns the company of this pattern.
*
* @return company of pattern
*/
public PatternDataField getCompany() {
return dataFields.get(1);
}
/**
* This method returns the item of this pattern.
*
* @return item of pattern
*/
public PatternDataField getItem() {
return dataFields.get(2);
}
/**
* This method returns the serial of this patern.
*
* @return serial of pattern
*/
public PatternDataField getSerial() {
return dataFields.get(3);
}
/**
* This method returns the data fields of this pattern.
*
* @return list of data fields
*/
public List<PatternDataField> getDataFields() {
return dataFields;
}
/**
* This method indicates if this pattern is disjoint to the specified pattern.
*
* @param pattern to check disjointness
* @return true if the patterns are disjoint and false otherwise
* @throws ECSpecValidationException if the pattern is invalid
*/
public boolean isDisjoint(String pattern) throws ECSpecValidationException {
return isDisjoint(new Pattern(pattern, PatternUsage.GROUP));
}
/**
* This method indicates if this pattern is disjoint to the specified pattern.
*
* @param pattern to chek disjointness
* @return true if the patterns are disjoint and false otherwise
* @throws ECSpecValidationException the pattern cannot be validated.
*/
public boolean isDisjoint(Pattern pattern) throws ECSpecValidationException {
if (!type.equals(pattern.getType())) {
// if types are different, then the patterns are disjoint
return true;
} else {
// if some corresponding data fields are disjoint, then the patterns are disjoint
for (int i = 0; i < dataFields.size(); i++) {
if (dataFields.get(i).isDisjoint(pattern.getDataFields().get(i))) {
return true;
}
}
return false;
}
}
/**
* This method indicates if a tag is member of this filter or group pattern.
* If the pattern is a tag pattern, the return value is null.
*
* @param tagURI to check for member
* @return true if tag is member of this pattern and false otherwise
* @throws ECSpecValidationException if the tag pattern is invalid
* @throws ImplementationException if an implementation exception occurs
*/
public boolean isMember(String tagURI) throws ECSpecValidationException, ImplementationException {
if (usage == PatternUsage.TAG) {
return false;
}
// create pattern of usage TAG ('*' and 'X' are not allowed)
Pattern tag = new Pattern(tagURI, PatternUsage.TAG);
// check type
if (tag.getType().equals(getType())) {
// get data fields
List<PatternDataField> tagDataFields = tag.getDataFields();
// check number of data fields
if (tagDataFields.size() == dataFields.size()) {
// check contents of data fields
for (int i = 0; i < dataFields.size(); i++) {
if (!dataFields.get(i).isMember(tagDataFields.get(i).getValue())) {
return false;
}
}
return true;
}
}
return false;
}
/**
* This method returns the group name for a tag depending on this group pattern.
* If the pattern is not a group pattern or the tag is not a member of this group pattern,
* the return value is null.
*
* @param tagURI to get the group name from
* @return group name
* @throws ImplementationException if an implementation exception occurs
* @throws ECSpecValidationException if the tag pattern is invalid
*/
public String getGroupName(String tagURI) throws ImplementationException, ECSpecValidationException {
if (usage != PatternUsage.GROUP || !isMember(tagURI)) {
return null;
}
try {
// create pattern of usage TAG ('*', 'X' and ranges are not allowed)
Pattern tag = new Pattern(tagURI, PatternUsage.TAG);
// clone pattern to create a group name
Pattern groupName = new Pattern(this.toString(), PatternUsage.GROUP);
// get data fields
List<PatternDataField> tagDataFields = tag.getDataFields();
List<PatternDataField> groupNameDataFields = groupName.getDataFields();
// replace 'X' in group name
for (int i = 0; i < groupNameDataFields.size(); i++) {
if (groupNameDataFields.get(i).isX()) {
groupNameDataFields.set(i, tagDataFields.get(i));
}
}
return groupName.toString();
} catch (ECSpecValidationException e) {
throw new ImplementationException(e.getMessage());
}
}
/**
* This method returns a string representation of this pattern.
*
* @return string representation
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(FIRST_FIELD);
buffer.append(":");
buffer.append(SECOND_FIELD);
buffer.append(":");
buffer.append(thirdField);
buffer.append(":");
buffer.append(type.toSring());
buffer.append(":");
for (PatternDataField dataField : dataFields) {
buffer.append(dataField.toString());
buffer.append(".");
}
return buffer.substring(0, buffer.length() - 1);
}
//
// private methods
//
/**
* This method parses the data fields of this pattern.
*
* @param dataFieldsString to parse
* @param pattern the whole pattern
* @throws ECSpecValidationException if the data field string is invalid
*/
private void parseDataFields(String dataFieldsString, String pattern) throws ECSpecValidationException {
// split data fields
String[] dataFieldsStringArray = dataFieldsString.split("\\.");
// check number of data fields
int nbrOfDataFields = type.getNumberOfDatafields();
if (dataFieldsStringArray.length < nbrOfDataFields) {
throw new ECSpecValidationException("Too less data fields '" + dataFieldsString + "' in pattern '" +
pattern +"'. Pattern Format '" + type + "' expects " + nbrOfDataFields + " data fields.");
} else if (dataFieldsStringArray.length > nbrOfDataFields) {
throw new ECSpecValidationException("Too many data fields '" + dataFieldsString + "' in pattern '" +
pattern + "'. Pattern Format '" + type + "' expects " + nbrOfDataFields + " data fields.");
}
// check format of each field
for (int i = 0; i < dataFieldsStringArray.length; i++) {
dataFields.add(new PatternDataField(dataFieldsStringArray[i], usage));
}
}
/**
* This method indicates, if the needle string is an element of the haystack string array.
*
* @param haystack string array which possibly contains the needle string
* @param needle string to search in the haystack string array
* @return true if the haystack contains the needle and false otherwise
*/
private boolean containsString(String[] haystack, String needle) {
if (needle == null) {
for (String element : haystack) {
if (element == null) {
return true;
}
}
return false;
} else {
boolean found = false;
for (String element : haystack) {
if (needle.equals(element)) {
found = true;
}
}
return found;
}
}
}