001: /*_############################################################################
002: _##
003: _## SNMP4J - OctetString.java
004: _##
005: _## Copyright (C) 2003-2008 Frank Fock and Jochen Katz (SNMP4J.org)
006: _##
007: _## Licensed under the Apache License, Version 2.0 (the "License");
008: _## you may not use this file except in compliance with the License.
009: _## You may obtain a copy of the License at
010: _##
011: _## http://www.apache.org/licenses/LICENSE-2.0
012: _##
013: _## Unless required by applicable law or agreed to in writing, software
014: _## distributed under the License is distributed on an "AS IS" BASIS,
015: _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: _## See the License for the specific language governing permissions and
017: _## limitations under the License.
018: _##
019: _##########################################################################*/
020:
021: package org.snmp4j.smi;
022:
023: import java.io.*;
024: import java.util.*;
025: import org.snmp4j.asn1.BER;
026: import org.snmp4j.asn1.BERInputStream;
027:
028: /**
029: * The <code>OctetString</code> class represents the SMI type OCTET STRING.
030: *
031: * @author Frank Fock
032: * @version 1.8
033: * @since 1.0
034: */
035: public class OctetString extends AbstractVariable implements
036: AssignableFromByteArray, AssignableFromString {
037:
038: private static final long serialVersionUID = 4125661211046256289L;
039:
040: private static final char DEFAULT_HEX_DELIMITER = ':';
041:
042: private byte[] value = new byte[0];
043:
044: /**
045: * Creates a zero length octet string.
046: */
047: public OctetString() {
048: }
049:
050: /**
051: * Creates an octet string from an byte array.
052: * @param rawValue
053: * an array of bytes.
054: */
055: public OctetString(byte[] rawValue) {
056: this (rawValue, 0, rawValue.length);
057: }
058:
059: /**
060: * Creates an octet string from an byte array.
061: * @param rawValue
062: * an array of bytes.
063: * @param offset
064: * the position (zero based) of the first byte to be copied from
065: * <code>rawValue</code>into the new <code>OctetSring</code>.
066: * @param length
067: * the number of bytes to be copied.
068: */
069: public OctetString(byte[] rawValue, int offset, int length) {
070: value = new byte[length];
071: System.arraycopy(rawValue, offset, value, 0, length);
072: }
073:
074: /**
075: * Creates an octet string from a java string.
076: *
077: * @param stringValue
078: * a Java string.
079: */
080: public OctetString(String stringValue) {
081: this .value = stringValue.getBytes();
082: }
083:
084: /**
085: * Creates an octet string from another OctetString by cloning its value.
086: *
087: * @param other
088: * an <code>OctetString</code> instance.
089: */
090: public OctetString(OctetString other) {
091: this .value = new byte[0];
092: append(other);
093: }
094:
095: /**
096: * Appends a single byte to this octet string.
097: * @param b
098: * a byte value.
099: */
100: public void append(byte b) {
101: byte[] newValue = new byte[value.length + 1];
102: System.arraycopy(value, 0, newValue, 0, value.length);
103: newValue[value.length] = b;
104: value = newValue;
105: }
106:
107: /**
108: * Appends an array of bytes to this octet string.
109: * @param bytes
110: * an array of bytes.
111: */
112: public void append(byte[] bytes) {
113: byte[] newValue = new byte[value.length + bytes.length];
114: System.arraycopy(value, 0, newValue, 0, value.length);
115: System
116: .arraycopy(bytes, 0, newValue, value.length,
117: bytes.length);
118: value = newValue;
119: }
120:
121: /**
122: * Appends an octet string.
123: * @param octetString
124: * an <code>OctetString</code> to append to this octet string.
125: */
126: public void append(OctetString octetString) {
127: append(octetString.getValue());
128: }
129:
130: /**
131: * Appends the supplied string to this <code>OctetString</code>. Calling this
132: * method is identical to <I>append(string.getBytes())</I>.
133: * @param string
134: * a String instance.
135: */
136: public void append(String string) {
137: append(string.getBytes());
138: }
139:
140: /**
141: * Sets the value of the octet string to a zero length string.
142: */
143: public void clear() {
144: value = new byte[0];
145: }
146:
147: public void encodeBER(OutputStream outputStream)
148: throws java.io.IOException {
149: BER.encodeString(outputStream, BER.OCTETSTRING, getValue());
150: }
151:
152: public void decodeBER(BERInputStream inputStream)
153: throws java.io.IOException {
154: BER.MutableByte type = new BER.MutableByte();
155: byte[] v = BER.decodeString(inputStream, type);
156: if (type.getValue() != BER.OCTETSTRING) {
157: throw new IOException(
158: "Wrong type encountered when decoding OctetString: "
159: + type.getValue());
160: }
161: setValue(v);
162: }
163:
164: public int getBERLength() {
165: return value.length + BER.getBERLengthOfLength(value.length)
166: + 1;
167: }
168:
169: public int getSyntax() {
170: return SMIConstants.SYNTAX_OCTET_STRING;
171: }
172:
173: /**
174: * Gets the byte at the specified index.
175: * @param index
176: * a zero-based index into the octet string.
177: * @return
178: * the byte value at the specified index.
179: * @throws ArrayIndexOutOfBoundsException
180: * if <code>index</code> < 0 or > {@link #length()}.
181: */
182: public final byte get(int index) {
183: return value[index];
184: }
185:
186: /**
187: * Sets the byte value at the specified index.
188: * @param index
189: * an index value greater or equal 0 and less than {@link #length()}.
190: * @param b
191: * the byte value to set.
192: * @since v1.2
193: */
194: public final void set(int index, byte b) {
195: value[index] = b;
196: }
197:
198: public int hashCode() {
199: int hash = 0;
200: for (int i = 0; i < value.length; i++) {
201: hash += value[i] * 31 ^ ((value.length - 1) - i);
202: }
203: return hash;
204: }
205:
206: public boolean equals(Object o) {
207: if (o instanceof OctetString) {
208: OctetString other = (OctetString) o;
209: return Arrays.equals(value, other.value);
210: } else if (o instanceof byte[]) {
211: return Arrays.equals(value, (byte[]) o);
212: }
213: return false;
214: }
215:
216: public int compareTo(Object o) {
217: if (o instanceof OctetString) {
218: OctetString other = (OctetString) o;
219: int maxlen = Math.min(value.length, other.value.length);
220: for (int i = 0; i < maxlen; i++) {
221: if (value[i] != other.value[i]) {
222: if ((value[i] & 0xFF) < (other.value[i] & 0xFF)) {
223: return -1;
224: } else {
225: return 1;
226: }
227: }
228: }
229: return (value.length - other.value.length);
230: }
231: throw new ClassCastException(o.getClass().getName());
232: }
233:
234: /**
235: * Returns a new string that is a substring of this string. The substring
236: * begins at the specified <code>beginIndex</code> and extends to the
237: * character at index <code>endIndex - 1</code>.
238: * Thus the length of the substring is <code>endIndex-beginIndex</code>.
239: * @param beginIndex
240: * the beginning index, inclusive.
241: * @param endIndex
242: * the ending index, exclusive.
243: * @return
244: * the specified substring.
245: * @since 1.3
246: */
247: public OctetString substring(int beginIndex, int endIndex) {
248: if ((beginIndex < 0) || (endIndex > length())) {
249: throw new IndexOutOfBoundsException();
250: }
251: byte[] substring = new byte[endIndex - beginIndex];
252: System.arraycopy(value, beginIndex, substring, 0,
253: substring.length);
254: return new OctetString(substring);
255: }
256:
257: /**
258: * Tests if this octet string starts with the specified prefix.
259: * @param prefix
260: * the prefix.
261: * @return
262: * <code>true</code> if the bytes of this octet string up to the length
263: * of <code>prefix</code> equal those of <code>prefix</code>.
264: * @since 1.2
265: */
266: public boolean startsWith(OctetString prefix) {
267: if ((prefix == null) || prefix.length() > length()) {
268: return false;
269: }
270: for (int i = 0; i < prefix.length(); i++) {
271: if (prefix.get(i) != value[i]) {
272: return false;
273: }
274: }
275: return true;
276: }
277:
278: /**
279: * Determines whether this octet string contains non ISO control characters
280: * only.
281: * @return
282: * <code>false</code> if this octet string contains any ISO control
283: * characters as defined by <code>Character.isISOControl(char)</code>
284: * except if these ISO control characters are all whitespace characters
285: * as defined by <code>Character.isWhitespace(char)</code>.
286: */
287: public boolean isPrintable() {
288: for (int i = 0; i < value.length; i++) {
289: char c = (char) value[i];
290: if ((Character.isISOControl(c) || ((value[i] & 0xFF) >= 0x80))
291: && (!Character.isWhitespace(c))) {
292: return false;
293: }
294: }
295: return true;
296: }
297:
298: public String toString() {
299: if (isPrintable()) {
300: return new String(value);
301: }
302: return toHexString();
303: }
304:
305: public String toHexString() {
306: return toHexString(DEFAULT_HEX_DELIMITER);
307: }
308:
309: public String toHexString(char separator) {
310: return toString(separator, 16);
311: }
312:
313: public static OctetString fromHexString(String hexString) {
314: return fromHexString(hexString, DEFAULT_HEX_DELIMITER);
315: }
316:
317: public static OctetString fromHexString(String hexString,
318: char delimiter) {
319: return OctetString.fromString(hexString, delimiter, 16);
320: }
321:
322: public static OctetString fromString(String string, char delimiter,
323: int radix) {
324: String delim = "";
325: delim += delimiter;
326: StringTokenizer st = new StringTokenizer(string, delim);
327: byte[] value = new byte[st.countTokens()];
328: for (int n = 0; st.hasMoreTokens(); n++) {
329: String s = st.nextToken();
330: value[n] = (byte) Integer.parseInt(s, radix);
331: }
332: return new OctetString(value);
333: }
334:
335: /**
336: * Creates an OctetString from a string represantation in the specified
337: * radix.
338: * @param string
339: * the string representation of an octet string.
340: * @param radix
341: * the radix of the string represantion.
342: * @return
343: * the OctetString instance.
344: * @since 1.6
345: */
346: public static OctetString fromString(String string, int radix) {
347: int digits = (int) (Math.round((float) Math.log(256)
348: / Math.log(radix)));
349: byte[] value = new byte[string.length() / digits];
350: for (int n = 0; n < string.length(); n += digits) {
351: String s = string.substring(n, n + digits);
352: value[n / digits] = (byte) Integer.parseInt(s, radix);
353: }
354: return new OctetString(value);
355: }
356:
357: public String toString(char separator, int radix) {
358: int digits = (int) (Math.round((float) Math.log(256)
359: / Math.log(radix)));
360: StringBuffer buf = new StringBuffer(value.length * (digits + 1));
361: for (int i = 0; i < value.length; i++) {
362: if (i > 0) {
363: buf.append(separator);
364: }
365: int v = (value[i] & 0xFF);
366: String val = Integer.toString(v, radix);
367: for (int j = 0; j < digits - val.length(); j++) {
368: buf.append('0');
369: }
370: buf.append(val);
371: }
372: return buf.toString();
373: }
374:
375: /**
376: * Returns a string representation of this octet string in the radix
377: * specified. There will be no separation characters, but each byte will
378: * be represented by <code>round(log(256)/log(radix))</code> digits.
379: *
380: * @param radix
381: * the radix to use in the string representation.
382: * @return
383: * a string representation of this ocetet string in the specified radix.
384: * @since 1.6
385: */
386: public String toString(int radix) {
387: int digits = (int) (Math.round((float) Math.log(256)
388: / Math.log(radix)));
389: StringBuffer buf = new StringBuffer(value.length * (digits + 1));
390: for (int i = 0; i < value.length; i++) {
391: int v = (value[i] & 0xFF);
392: String val = Integer.toString(v, radix);
393: for (int j = 0; j < digits - val.length(); j++) {
394: buf.append('0');
395: }
396: buf.append(val);
397: }
398: return buf.toString();
399: }
400:
401: /**
402: * Formats the content into a ASCII string. Non-printable characters are
403: * replaced by the supplied placeholder character.
404: * @param placeholder
405: * a placeholder character, for example '.'.
406: * @return
407: * the contents of this octet string as ASCII formatted string.
408: * @since 1.6
409: */
410: public String toASCII(char placeholder) {
411: StringBuffer buf = new StringBuffer(value.length);
412: for (int i = 0; i < value.length; i++) {
413: if ((Character.isISOControl((char) value[i]))
414: || ((value[i] & 0xFF) >= 0x80)) {
415: buf.append(placeholder);
416: } else {
417: buf.append((char) value[i]);
418: }
419: }
420: return buf.toString();
421: }
422:
423: public void setValue(String value) {
424: setValue(value.getBytes());
425: }
426:
427: public void setValue(byte[] value) {
428: if (value == null) {
429: throw new IllegalArgumentException(
430: "OctetString must not be assigned a null value");
431: }
432: this .value = value;
433: }
434:
435: public byte[] getValue() {
436: return value;
437: }
438:
439: /**
440: * Gets the length of the byte string.
441: * @return
442: * an integer >= 0.
443: */
444: public final int length() {
445: return value.length;
446: }
447:
448: public Object clone() {
449: return new OctetString(value);
450: }
451:
452: /**
453: * Returns the length of the payload of this <code>BERSerializable</code>
454: * object in bytes when encoded according to the Basic Encoding Rules (BER).
455: *
456: * @return the BER encoded length of this variable.
457: */
458: public int getBERPayloadLength() {
459: return value.length;
460: }
461:
462: public int toInt() {
463: throw new UnsupportedOperationException();
464: }
465:
466: public long toLong() {
467: throw new UnsupportedOperationException();
468: }
469:
470: /**
471: * Returns a copy of this OctetString where each bit not set in the supplied
472: * mask zeros the corresponding bit in the returned OctetString.
473: * @param mask
474: * a mask where the n-th bit corresponds to the n-th bit in the returned
475: * OctetString.
476: * @return
477: * the masked OctetString.
478: * @since 1.7
479: */
480: public OctetString mask(OctetString mask) {
481: byte[] masked = new byte[value.length];
482: System.arraycopy(value, 0, masked, 0, value.length);
483: for (int i = 0; (i < mask.length()) && (i < masked.length); i++) {
484: masked[i] = (byte) (masked[i] & mask.get(i));
485: }
486: return new OctetString(masked);
487: }
488:
489: public OID toSubIndex(boolean impliedLength) {
490: int[] subIndex;
491: int offset = 0;
492: if (!impliedLength) {
493: subIndex = new int[length() + 1];
494: subIndex[offset++] = length();
495: } else {
496: subIndex = new int[length()];
497: }
498: for (int i = 0; i < length(); i++) {
499: subIndex[offset + i] = get(i) & 0xFF;
500: }
501: return new OID(subIndex);
502: }
503:
504: public void fromSubIndex(OID subIndex, boolean impliedLength) {
505: if (impliedLength) {
506: setValue(subIndex.toByteArray());
507: } else {
508: OID suffix = new OID(subIndex.getValue(), 1, subIndex
509: .size() - 1);
510: setValue(suffix.toByteArray());
511: }
512: }
513:
514: /**
515: * Splits an <code>OctetString</code> using a set of delimiter characters
516: * similar to how a StringTokenizer would do it.
517: * @param octetString
518: * the input string to tokenize.
519: * @param delimOctets
520: * a set of delimiter octets.
521: * @return
522: * a Collection of OctetString instances that contain the tokens.
523: */
524: public static final Collection split(OctetString octetString,
525: OctetString delimOctets) {
526: List parts = new LinkedList();
527: int maxDelim = -1;
528: for (int i = 0; i < delimOctets.length(); i++) {
529: int delim = delimOctets.get(i) & 0xFF;
530: if (delim > maxDelim) {
531: maxDelim = delim;
532: }
533: }
534: int startPos = 0;
535: for (int i = 0; i < octetString.length(); i++) {
536: int c = octetString.value[i] & 0xFF;
537: boolean isDelim = false;
538: if (c <= maxDelim) {
539: for (int j = 0; j < delimOctets.length(); j++) {
540: if (c == (delimOctets.get(j) & 0xFF)) {
541: if ((startPos >= 0) && (i > startPos)) {
542: parts.add(new OctetString(
543: octetString.value, startPos, i
544: - startPos));
545: }
546: startPos = -1;
547: isDelim = true;
548: }
549: }
550: }
551: if (!isDelim && (startPos < 0)) {
552: startPos = i;
553: }
554: }
555: if (startPos >= 0) {
556: parts.add(new OctetString(octetString.value, startPos,
557: octetString.length() - startPos));
558: }
559: return parts;
560: }
561:
562: /**
563: * Creates an <code>OctetString</code> from an byte array.
564: * @param value
565: * a byte array that is copied into the value of the created
566: * <code>OctetString</code> or <code>null</code>.
567: * @return
568: * an OctetString or <code>null</code> if <code>value</code>
569: * is <code>null</code>.
570: * @since 1.7
571: */
572: public static OctetString fromByteArray(byte[] value) {
573: if (value == null) {
574: return null;
575: }
576: return new OctetString(value);
577: }
578:
579: public byte[] toByteArray() {
580: return getValue();
581: }
582: }
|