/***************************************************************************
*
* This file is part of the 'NDEF Tools for Android' project at
* http://code.google.com/p/ndef-tools-for-android/
*
* 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.ndeftools.wellknown;
import java.util.ArrayList;
import java.util.List;
import org.ndeftools.Message;
import org.ndeftools.Record;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
/**
* Generic control record.<br/><br/>
*
* The Generic Control Record Type Definition defines an NFC Forum Well Known Type on how
* to activate a specific application or to set a property in a destination device through NFC
* communication.<br/><br/>
*
* The purpose of the Generic Control RTD is to provide a way to request a specific action to an
* NFC Forum device (a destination device) from another NFC Forum device, a tag, or a card
* (source device) through NFC communication. A MIME type record in an NDEF message can
* provide access to an associated function; however, the association is defined solely in the
* destination device. It implies that a function to be accessed may differ from device to device
* when more than one function shares the same MIME type. To prevent such uncertainty, the
* Generic Control RTD allows the message issuer to specify specific functions to be accessed with
* the message. Moreover, the NDEF parser does not need to resolve the association of data and
* functions.
*
* @author Adrian Stabiszewski (as@nfctools.org)
*
*/
public class GenericControlRecord extends Record {
private static final byte CB_CHECK_EXIT_CONDITION = 0x02;
private static final byte CB_IGNORE_FOLLOWING_IF_FAILED = 0x04;
public static byte[] type = {'G', 'c'};
public static GenericControlRecord parseNdefRecord(NdefRecord ndefRecord) throws FormatException {
byte[] payload = ndefRecord.getPayload();
normalizeMessageBeginEnd(payload, 1, payload.length -1);
Message payloadNdefMessage = Message.parseNdefMessage(payload, 1, payload.length - 1);
GenericControlRecord genericControlRecord = new GenericControlRecord();
genericControlRecord.setConfigurationByte(payload[0]);
for (Record record : payloadNdefMessage) {
if (record instanceof GcTargetRecord) {
genericControlRecord.setTarget((GcTargetRecord)record);
} else if (record instanceof GcActionRecord) {
genericControlRecord.setAction((GcActionRecord)record);
} else if (record instanceof GcDataRecord) {
genericControlRecord.setData((GcDataRecord)record);
} else {
throw new IllegalArgumentException("Unexpected record " + record.getClass().getName());
}
}
if (!genericControlRecord.hasTarget()) {
throw new IllegalArgumentException("Expected target record");
}
return genericControlRecord;
}
private byte configurationByte;
private GcTargetRecord target;
private GcActionRecord action;
private GcDataRecord data;
public GenericControlRecord(GcTargetRecord target, byte configurationByte) {
this.target = target;
this.configurationByte = configurationByte;
}
public GenericControlRecord(byte configurationByte,
GcTargetRecord target,
GcActionRecord action,
GcDataRecord data) {
this.configurationByte = configurationByte;
this.target = target;
this.action = action;
this.data = data;
}
public GenericControlRecord() {
}
public void setConfigurationByte(byte configurationByte) {
this.configurationByte = configurationByte;
}
public byte getConfigurationByte() {
return configurationByte;
}
public boolean isIgnoreFollowingIfFailed() {
return (configurationByte & CB_IGNORE_FOLLOWING_IF_FAILED) != 0;
}
public void setIgnoreFollowingIfFailed() {
configurationByte |= CB_IGNORE_FOLLOWING_IF_FAILED;
}
public boolean isCheckExitCondition() {
return (configurationByte & CB_CHECK_EXIT_CONDITION) != 0;
}
public void setCheckExitCondition() {
configurationByte |= CB_CHECK_EXIT_CONDITION;
}
public GcTargetRecord getTarget() {
return target;
}
public void setTarget(GcTargetRecord target) {
this.target = target;
}
public GcActionRecord getAction() {
return action;
}
public void setAction(GcActionRecord action) {
this.action = action;
}
public GcDataRecord getData() {
return data;
}
public void setData(GcDataRecord data) {
this.data = data;
}
public boolean hasTarget() {
return target != null;
}
public boolean hasAction() {
return action != null;
}
public boolean hasData() {
return data != null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((action == null) ? 0 : action.hashCode());
result = prime * result + configurationByte;
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((target == null) ? 0 : target.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
GenericControlRecord other = (GenericControlRecord)obj;
if (action == null) {
if (other.action != null)
return false;
}
else if (!action.equals(other.action))
return false;
if (configurationByte != other.configurationByte)
return false;
if (data == null) {
if (other.data != null)
return false;
}
else if (!data.equals(other.data))
return false;
if (target == null) {
if (other.target != null)
return false;
}
else if (!target.equals(other.target))
return false;
return true;
}
@Override
public NdefRecord getNdefRecord() {
if(!hasTarget()) {
throw new IllegalArgumentException("Expected target");
}
List<NdefRecord> records = new ArrayList<NdefRecord>();
records.add(target.getNdefRecord());
if (hasAction()) {
records.add(action.getNdefRecord());
}
if (hasData()) {
records.add(data.getNdefRecord());
}
byte[] array = new NdefMessage(records.toArray(new NdefRecord[records.size()])).toByteArray();
byte[] payload = new byte[array.length + 1];
payload[0] = configurationByte;
System.arraycopy(array, 0, payload, 1, array.length);
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, type, id != null ? id : EMPTY, payload);
}
}