package ua.stu.scplib.attribute; /** * <p>The {@link com.pixelmed.dicom.AttributeFactory AttributeFactory} class is a factory class of static methods for creating * concrete instances of the abstract class {@link com.pixelmed.dicom.Attribute Attribute} based on their * value representation.</p> * * <p>This class is * primarily used when reading a parsing a DICOM dataset, and needing to create attributes based on their * value representation, either from a dictionary or from the explicit value representation in the dataset.</p> * * @see com.pixelmed.dicom.Attribute * @see com.pixelmed.dicom.AttributeList * * @author dclunie */ public class AttributeFactory { private static final long maximumInMemoryPixelDataValueLength = 4096*4096*2; private AttributeFactory() {} /** * <p>A static method to determine the {@link java.lang.Class Class} appropriate for storing an attribute based on the supplied value representation.</p> * * @param tag the {@link com.pixelmed.dicom.AttributeTag AttributeTag} tag of the attribute (to check whether or not pixel data) * @param vr the value representation of the attribute * @param explicit a flag indicating that the stream to read or write uses explicit value representation (affects pixel data encoding choice) * @param bytesPerSample 1 or 2 bytes per sample indicating whether to use OB or OW for pixel data * @param leaveOtherDataOnDisk whether or not to leave OB or OW on disk or read it into memory * * @return the class appropriate for the attribute */ public static Class getClassOfAttributeFromValueRepresentation(AttributeTag tag,byte[] vr,boolean explicit,int bytesPerSample,boolean leaveOtherDataOnDisk) { Class c; Class classForOtherByte = leaveOtherDataOnDisk ? OtherByteAttributeOnDisk.class : OtherByteAttribute.class; Class classForOtherWord = leaveOtherDataOnDisk ? OtherWordAttributeOnDisk.class : OtherWordAttribute.class; if (ValueRepresentation.isApplicationEntityVR(vr)) { c=ApplicationEntityAttribute.class; } else if (ValueRepresentation.isAgeStringVR(vr)) { c=AgeStringAttribute.class; } else if (ValueRepresentation.isAttributeTagVR(vr)) { c=AttributeTagAttribute.class; } else if (ValueRepresentation.isCodeStringVR(vr)) { c=CodeStringAttribute.class; } else if (ValueRepresentation.isDateVR(vr)) { c=DateAttribute.class; } else if (ValueRepresentation.isDateTimeVR(vr)) { c=DateTimeAttribute.class; } else if (ValueRepresentation.isDecimalStringVR(vr)) { c=DecimalStringAttribute.class; } else if (ValueRepresentation.isFloatDoubleVR(vr)) { c=FloatDoubleAttribute.class; } else if (ValueRepresentation.isFloatSingleVR(vr)) { c=FloatSingleAttribute.class; } else if (ValueRepresentation.isIntegerStringVR(vr)) { c=IntegerStringAttribute.class; } else if (ValueRepresentation.isLongStringVR(vr)) { c=LongStringAttribute.class; } else if (ValueRepresentation.isLongTextVR(vr)) { c=LongTextAttribute.class; } else if (ValueRepresentation.isOtherByteVR(vr)) { // just in case was incorrectly encoded as explicit OB VR but with bytesPerSample > 1 (Bits Allocated > 8) c=(bytesPerSample > 1 && tag.equals(TagFromName.PixelData)) ? classForOtherWord : classForOtherByte; } else if (ValueRepresentation.isOtherFloatVR(vr)) { c=OtherFloatAttribute.class; } else if (ValueRepresentation.isOtherWordVR(vr)) { // This is not quite right ... in implicit VR, pixel data is always OW theoretically, // but this saves later unpacking ... and works as long is implicit VR is little endian c=(bytesPerSample > 1 || !tag.equals(TagFromName.PixelData)) ? classForOtherWord : classForOtherByte; } else if (ValueRepresentation.isOtherUnspecifiedVR(vr)) { c=(bytesPerSample > 1 || !tag.equals(TagFromName.PixelData)) ? classForOtherWord : classForOtherByte; } else if (ValueRepresentation.isPersonNameVR(vr)) { c=PersonNameAttribute.class; } else if (ValueRepresentation.isShortStringVR(vr)) { c=ShortStringAttribute.class; } else if (ValueRepresentation.isSignedLongVR(vr)) { c=SignedLongAttribute.class; } else if (ValueRepresentation.isSignedShortVR(vr)) { c=SignedShortAttribute.class; } else if (ValueRepresentation.isShortTextVR(vr)) { c=ShortTextAttribute.class; } else if (ValueRepresentation.isTimeVR(vr)) { c=TimeAttribute.class; } else if (ValueRepresentation.isUniqueIdentifierVR(vr)) { c=UniqueIdentifierAttribute.class; } else if (ValueRepresentation.isUnsignedLongVR(vr)) { c=UnsignedLongAttribute.class; } else if (ValueRepresentation.isUnknownVR(vr)) { c=UnknownAttribute.class; } else if (ValueRepresentation.isUnsignedShortVR(vr)) { c=UnsignedShortAttribute.class; } else if (ValueRepresentation.isUnspecifiedShortVR(vr)) { c=UnsignedShortAttribute.class; // treat as unsigned for now ... should choose on PixelRepresentation } else if (ValueRepresentation.isUnspecifiedShortOrOtherWordVR(vr)) { c=UnsignedShortAttribute.class; // treat as unsigned for now ... should choose on PixelRepresentation } else if (ValueRepresentation.isUnlimitedTextVR(vr)) { c=UnlimitedTextAttribute.class; } else { // unrecognized but fixed length VR ... treat as UN ... c=UnknownAttribute.class; } return c; } /** * <p>A static method to create an {@link com.pixelmed.dicom.Attribute Attribute} based on the supplied value representation.</p> * * @param tag the {@link com.pixelmed.dicom.AttributeTag AttributeTag} tag of the attribute to create * @param vr the value representation of the attribute to create (such as read from the stream, or from the dictionary) * * @return the attribute of an appropriate class, with no value * @exception DicomException */ public static Attribute newAttribute(AttributeTag tag,byte[] vr) throws DicomException { return newAttribute(tag,vr,true/*explicit*/,0/*bytesPerSample*/); } /** * <p>A static method to create an {@link com.pixelmed.dicom.Attribute Attribute} based on the supplied value representation.</p> * * @param tag the {@link com.pixelmed.dicom.AttributeTag AttributeTag} tag of the attribute to create * @param vr the value representation of the attribute to create (such as read from the stream, or from the dictionary) * @param specificCharacterSet the {@link com.pixelmed.dicom.SpecificCharacterSet SpecificCharacterSet} to be used text values * * @return the attribute of an appropriate class, with no value * @exception DicomException */ public static Attribute newAttribute(AttributeTag tag,byte[] vr,SpecificCharacterSet specificCharacterSet) throws DicomException { return newAttribute(tag,vr,specificCharacterSet,true/*explicit*/,0/*bytesPerSample*/); } /** * <p>A static method to create an {@link com.pixelmed.dicom.Attribute Attribute} based on the supplied value representation.</p> * * @param tag the {@link com.pixelmed.dicom.AttributeTag AttributeTag} tag of the attribute to create * @param vr the value representation of the attribute to create (such as read from the stream, or from the dictionary) * @param explicit a flag indicating that the stream is explicit value representation (affects pixel data encoding choice) * @param bytesPerSample 1 or 2 bytes per sample indicating whether to use OB or OW for pixel data * * @return the attribute of an appropriate class, with no value * @exception DicomException */ public static Attribute newAttribute(AttributeTag tag,byte[] vr,boolean explicit,int bytesPerSample) throws DicomException { return newAttribute(tag,vr,null/*SpecificCharacterSet*/,explicit,bytesPerSample); } /** * <p>A static method to create an {@link com.pixelmed.dicom.Attribute Attribute} based on the supplied value representation.</p> * * @param tag the {@link com.pixelmed.dicom.AttributeTag AttributeTag} tag of the attribute to create * @param vr the value representation of the attribute to create (such as read from the stream, or from the dictionary) * @param specificCharacterSet the {@link com.pixelmed.dicom.SpecificCharacterSet SpecificCharacterSet} to be used text values * @param explicit a flag indicating that the stream is explicit value representation (affects pixel data encoding choice) * @param bytesPerSample 1 or 2 bytes per sample indicating whether to use OB or OW for pixel data * * @return the attribute of an appropriate class, with no value * @exception DicomException */ public static Attribute newAttribute(AttributeTag tag,byte[] vr,SpecificCharacterSet specificCharacterSet,boolean explicit,int bytesPerSample) throws DicomException { Attribute a = null; try { Class classToUse = getClassOfAttributeFromValueRepresentation(tag,vr,explicit,bytesPerSample,false); Class [] argTypes = null; Object[] argValues = null; if (ValueRepresentation.isAffectedBySpecificCharacterSet(vr)) { Class [] t = {AttributeTag.class,SpecificCharacterSet.class}; Object[] v = {tag,specificCharacterSet}; argTypes=t; argValues=v; } else { Class [] t = {AttributeTag.class}; Object[] v = {tag}; argTypes=t; argValues=v; } a = (Attribute)(classToUse.getConstructor(argTypes).newInstance(argValues)); } catch (Exception e) { throw new DicomException("Could not instantiate an attribute for "+tag+": "+e.getCause()); } return a; } /** * <p>A static method to create and read an {@link com.pixelmed.dicom.Attribute Attribute} from a {@link com.pixelmed.dicom.DicomInputStream DicomInputStream}.</p> * * <p>The stream is left positioned at the start of the next attribute.</p> * * @param tag the {@link com.pixelmed.dicom.AttributeTag AttributeTag} tag of the attribute to create (already read from the stream) * @param vr the value representation of the attribute to create (already read from the stream, if present, else from the dictionary) * @param vl the value length of the attribute to create (already read from the stream) * @param i the {@link com.pixelmed.dicom.DicomInputStream DicomInputStream} to read the attribute from, positioned at the start of the value(s) to read * @param specificCharacterSet the {@link com.pixelmed.dicom.SpecificCharacterSet SpecificCharacterSet} to be used text values * @param explicit a flag indicating that the stream is explicit value representation (affects pixel data encoding choice) * @param bytesPerSample 1 or 2 bytes per sample indicating whether to use OB or OW for pixel data * @param byteOffset the byte offset from the beginning of the {@link com.pixelmed.dicom.DicomInputStream DicomInputStream} * * @return the attribute of an appropriate class populated with the value(s) read from the stream * @exception DicomException */ public static Attribute newAttribute(AttributeTag tag,byte[] vr,long vl,DicomInputStream i,SpecificCharacterSet specificCharacterSet, boolean explicit,int bytesPerSample,long byteOffset) throws DicomException { Attribute a = null; try { boolean leaveOtherDataOnDisk = tag.equals(TagFromName.PixelData) && bytesPerSample > 1 && vl > maximumInMemoryPixelDataValueLength && i.getFile() != null; Class classToUse = getClassOfAttributeFromValueRepresentation(tag,vr,explicit,bytesPerSample,leaveOtherDataOnDisk); Class [] argTypes = null; Object[] argValues = null; Long lvl = new Long(vl); Long lbo = new Long(byteOffset); if (ValueRepresentation.isAffectedBySpecificCharacterSet(vr)) { Class [] t = {AttributeTag.class,Long.class,DicomInputStream.class,SpecificCharacterSet.class}; Object[] v = {tag,lvl,i,specificCharacterSet}; argTypes=t; argValues=v; } else if (classToUse == OtherByteAttributeOnDisk.class || classToUse == OtherWordAttributeOnDisk.class) { Class [] t = {AttributeTag.class,Long.class,DicomInputStream.class,Long.class}; Object[] v = {tag,lvl,i,lbo}; argTypes=t; argValues=v; } else { Class [] t = {AttributeTag.class,long.class,DicomInputStream.class}; Object[] v = {tag,lvl,i}; argTypes=t; argValues=v; } a = (Attribute)(classToUse.getConstructor(argTypes).newInstance(argValues)); } catch (Exception e) { e.printStackTrace(System.err); throw new DicomException("Could not instantiate an attribute for "+tag+": "+e.getCause()); } return a; } }