blob: 6fa23f8baab8602fd607e5cdba16c0153087a7bf [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 common.asn1;
import java.util.Arrays;
import streamer.ByteBuffer;
/**
* One or more elements of different types.
*
* Only prefixed tags are supported.
*/
public class Sequence extends Tag {
public Tag[] tags;
public Sequence(String name) {
super(name);
tagType = SEQUENCE;
// Sequence and SequenceOf are always encoded as constructed
constructed = true;
}
@Override
public long calculateLengthOfValuePayload() {
long sum = 0;
for (Tag tag : tags) {
long tagLength = tag.calculateFullLength();
sum += tagLength;
}
return sum;
}
@Override
public void writeTagValuePayload(ByteBuffer buf) {
// Write tags
for (Tag tag : tags) {
tag.writeTag(buf);
}
}
@Override
public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
// Type is already read by parent parser
long length = buf.readBerLength();
if (length > buf.remainderLength())
throw new RuntimeException("BER sequence is too long: " + length + " bytes, while buffer remainder length is " + buf.remainderLength() + ". Data: " + buf
+ ".");
ByteBuffer value = buf.readBytes((int)length);
parseContent(value);
value.unref();
}
protected void parseContent(ByteBuffer buf) {
for (int i = 0; buf.remainderLength() > 0 && i < tags.length; i++) {
BerType typeAndFlags = readBerType(buf);
// If current tag does not match data in buffer
if (!tags[i].isTypeValid(typeAndFlags)) {
// If tag is required, then throw exception
if (!tags[i].optional) {
throw new RuntimeException("[" + this + "] ERROR: Required tag is missed: " + tags[i] + ". Unexected tag type: " + typeAndFlags + ". Data: " + buf
+ ".");
} else {
// One or more tags are omitted, so skip them
for (; i < tags.length; i++) {
if (tags[i].isTypeValid(typeAndFlags)) {
break;
}
}
if (i >= tags.length || !tags[i].isTypeValid(typeAndFlags)) {
throw new RuntimeException("[" + this + "] ERROR: No more tags to read or skip, but some data still left in buffer. Unexected tag type: "
+ typeAndFlags + ". Data: " + buf + ".");
}
}
}
tags[i].readTag(buf, typeAndFlags);
}
}
@Override
public boolean isTypeValid(BerType typeAndFlags, boolean explicit) {
if (explicit)
return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber;
else
// Sequences are always encoded as "constructed" in BER.
return typeAndFlags.tagClass == UNIVERSAL_CLASS && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == SEQUENCE;
}
@Override
public Tag deepCopy(String suffix) {
return new Sequence(name + suffix).copyFrom(this);
}
@Override
public Tag copyFrom(Tag tag) {
super.copyFrom(tag);
if (tags.length != ((Sequence)tag).tags.length)
throw new RuntimeException("Incompatible sequences. This: " + this + ", another: " + tag + ".");
for (int i = 0; i < tags.length; i++) {
tags[i].copyFrom(((Sequence)tag).tags[i]);
}
return this;
}
@Override
public String toString() {
return super.toString() + "{" + Arrays.toString(tags) + " }";
}
@Override
public boolean isValueSet() {
return tags != null;
}
}