package ua.stu.scplib.attribute; import java.io.*; import java.text.NumberFormat; /** * <p>An abstract class specializing {@link com.pixelmed.dicom.Attribute Attribute} for * the family of text attributes.</p> * * @author dclunie */ abstract public class TextAttribute extends Attribute { /***/ protected SpecificCharacterSet specificCharacterSet; /***/ String values[]; /** * <p>Decode a byte array into a string.</p> * * @param bytes the byte buffer in which the encoded string is located * @param offset the offset into the buffer * @param length the number of bytes to be decoded * @return the string decoded according to the specified or default specific character set */ protected String translateByteArrayToString(byte[] bytes,int offset,int length) { // NOT static return specificCharacterSet == null ? new String(bytes,0,length) : specificCharacterSet.translateByteArrayToString(bytes,0,length); } /** * <p>Encode a string into a byte array.</p> * * @param string the string to be encoded * @return the byte array encoded according to the specified or default specific character set * @exception UnsupportedEncodingException */ protected byte[] translateStringToByteArray(String string) throws UnsupportedEncodingException { // NOT static return specificCharacterSet == null ? string.getBytes() : specificCharacterSet.translateStringToByteArray(string); } /** * <p>Construct an (empty) attribute; called only by concrete sub-classes.</p> * * @param t the tag of the attribute */ protected TextAttribute(AttributeTag t) { super(t); doCommonConstructorStuff(null); } /** * <p>Construct an (empty) attribute; called only by concrete sub-classes.</p> * * @param t the tag of the attribute * @param specificCharacterSet the character set to be used for the text */ protected TextAttribute(AttributeTag t,SpecificCharacterSet specificCharacterSet) { super(t); doCommonConstructorStuff(specificCharacterSet); } /** * <p>Read an attribute from an input stream; called only by concrete sub-classes.</p> * * @param t the tag of the attribute * @param vl the value length of the attribute * @param i the input stream * @param specificCharacterSet the character set to be used for the text * @exception IOException * @exception DicomException */ protected TextAttribute(AttributeTag t,long vl,DicomInputStream i,SpecificCharacterSet specificCharacterSet) throws IOException, DicomException { super(t); doCommonConstructorStuff(vl,i,specificCharacterSet); } /** * <p>Read an attribute from an input stream; called only by concrete sub-classes.</p> * * @param t the tag of the attribute * @param vl the value length of the attribute * @param i the input stream * @param specificCharacterSet the character set to be used for the text * @exception IOException * @exception DicomException */ protected TextAttribute(AttributeTag t,Long vl,DicomInputStream i,SpecificCharacterSet specificCharacterSet) throws IOException, DicomException { super(t); doCommonConstructorStuff(vl.longValue(),i,specificCharacterSet); } /** * @param specificCharacterSet */ private void doCommonConstructorStuff(SpecificCharacterSet specificCharacterSet) { values=null; this.specificCharacterSet=specificCharacterSet; } /** * @param vl * @param i * @param specificCharacterSet * @exception IOException * @exception DicomException */ private void doCommonConstructorStuff(long vl,DicomInputStream i,SpecificCharacterSet specificCharacterSet) throws IOException, DicomException { doCommonConstructorStuff(specificCharacterSet); if (vl > 0) { byte[] buffer = new byte[(int)vl]; try { i.readInsistently(buffer,0,(int)vl); } catch (IOException e) { throw new DicomException("Failed to read value (length "+vl+" dec) in "+ValueRepresentation.getAsString(getVR())+" attribute "+getTag()); } String sbuf = translateByteArrayToString(buffer,0,(int)vl); vl=sbuf.length(); // NB. this only makes a difference for multi-byte character sets addValue(sbuf); } } /***/ public long getPaddedVL() { long vl = getVL(); if (vl%2 != 0) ++vl; return vl; } /** * <p>Get the appropriate byte for padding a string to an even length.</p> * * @return the byte pad value appropriate to the VR */ private byte getPadByte() { return 0x20; } /** * @exception DicomException */ private byte[] getPaddedByteValues() throws DicomException { String[] v = getStringValues(); //byte[] b = v == null ? null : v[0].getBytes(); byte[] b = null; try { if (v != null) b = translateStringToByteArray(v[0]); } catch (UnsupportedEncodingException e) { throw new DicomException("Unsupported encoding:"+e); } // should padding take into account character set, i.e. could the pad character be different ? :( if (b != null) { int bl = b.length; if (bl%2 != 0) { byte[] b2 = new byte[bl+1]; System.arraycopy(b,0,b2,0,bl); b2[bl]=getPadByte(); b=b2; } if (getPaddedVL() != b.length) { throw new DicomException("Internal error - byte array length not equal to expected padded VL"); } } return b; } /***/ public String toString(DicomDictionary dictionary) { StringBuffer str = new StringBuffer(); str.append(super.toString(dictionary)); str.append(" <"); try { String[] v = getStringValues(); if (v != null) str.append(v[0]); } catch (DicomException e) { str.append("XXXX"); } str.append(">"); return str.toString(); } /** * @param format the format to use for each numerical or decimal value * @exception DicomException */ public String[] getStringValues(NumberFormat format) throws DicomException { // ignore number format for generic text attributes return values; } /** * @param v * @exception DicomException */ public void addValue(String v) throws DicomException { if (values != null || valueMultiplicity > 0) throw new DicomException("No more than one value allowed for text attributes"); values=new String[1]; values[0]=v; valueLength=v.length(); ++valueMultiplicity; } /** * @exception DicomException */ public void removeValues() throws DicomException { valueLength=0; valueMultiplicity=0; values=null; } }