class DWARFAttributeFactory {

	public DWARFAttributeValue read(BinaryReader reader, DWARFCompilationUnit unit, DWARFForm form)
			throws IOException {
		StringTable debugStrings = prog.getDebugStrings();
		switch (form) {
			case DW_FORM_addr:
				return new DWARFNumericAttribute(
					DWARFUtil.readVarSizedULong(reader, unit.getPointerSize()));
			case DW_FORM_ref1: {
				long uoffset = DWARFUtil.readVarSizedULong(reader, 1);
				return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
			}
			case DW_FORM_ref2: {
				long uoffset = DWARFUtil.readVarSizedULong(reader, 2);
				return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
			}
			case DW_FORM_ref4: {
				long uoffset = DWARFUtil.readVarSizedULong(reader, 4);
				return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
			}
			case DW_FORM_ref8: {
				long uoffset = DWARFUtil.readVarSizedULong(reader, 8);
				return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
			}
			case DW_FORM_ref_udata: {
				long uoffset = LEB128.decode(reader, false);
				return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
			}

				// DW_FORM_ref_addr and DW_FORM_sec_offset have identical raw forms,
				// but point to different items (ref_addr points to elements in .debug_info,
				// sec_offset points to elements in other sections) 
			case DW_FORM_ref_addr:
				return new DWARFNumericAttribute(
					DWARFUtil.readOffsetByDWARFformat(reader, unit.getFormat()));
			case DW_FORM_sec_offset:
				return new DWARFNumericAttribute(
					DWARFUtil.readOffsetByDWARFformat(reader, unit.getFormat()));

			case DW_FORM_block1: {
				int length = DWARFUtil.readVarSizedUInt(reader, 1);
				return new DWARFBlobAttribute(reader.readNextByteArray(length));
			}
			case DW_FORM_block2: {
				int length = DWARFUtil.readVarSizedUInt(reader, 2);
				return new DWARFBlobAttribute(reader.readNextByteArray(length));
			}
			case DW_FORM_block4: {
				int length = DWARFUtil.readVarSizedUInt(reader, 4);
				if (length < 0 || length > MAX_BLOCK4_SIZE) {
					throw new IOException("Invalid/bad dw_form_block4 size: " + length);
				}
				return new DWARFBlobAttribute(reader.readNextByteArray(length));
			}
			case DW_FORM_block: {
				int length = LEB128.decode32u(reader);
				if (length < 0 || length > MAX_BLOCK4_SIZE) {
					throw new IOException("Invalid/bad dw_form_block size: " + length);
				}
				return new DWARFBlobAttribute(reader.readNextByteArray(length));
			}
			case DW_FORM_data1:
				return new DWARFAmbigNumericAttribute(reader.readNextByte(),
					NumberUtil.UNSIGNED_BYTE_MASK);
			case DW_FORM_data2:
				return new DWARFAmbigNumericAttribute(reader.readNextShort(),
					NumberUtil.UNSIGNED_SHORT_MASK);
			case DW_FORM_data4:
				return new DWARFAmbigNumericAttribute(reader.readNextInt(),
					NumberUtil.UNSIGNED_INT_MASK);
			case DW_FORM_data8:
				return new DWARFNumericAttribute(reader.readNextLong());
			case DW_FORM_sdata:
				return new DWARFNumericAttribute(LEB128.decode(reader, true));
			case DW_FORM_udata:
				return new DWARFNumericAttribute(LEB128.decode(reader, false));

			case DW_FORM_exprloc: {
				int length = LEB128.decode32u(reader);
				if (length < 0 || length > MAX_BLOCK4_SIZE) {
					throw new IOException("Invalid/bad dw_form_exprloc size: " + length);
				}
				return new DWARFBlobAttribute(reader.readNextByteArray(length));
			}

			case DW_FORM_flag:
				return DWARFBooleanAttribute.get(reader.readNextByte() != 0);
			case DW_FORM_flag_present:
				return DWARFBooleanAttribute.TRUE;

			case DW_FORM_string:
				return new DWARFStringAttribute(reader.readNextNullTerminatedAsciiString());
			case DW_FORM_strp:
				// Note: we can either read the string from the string section (via. the 
				// string table) here and put it in a DWARFStringAttribute and hope
				// it is used and not wasted memory, or use a DWARFDeferredStringAttribute 
				// to hold the offset until the string is actually requested in DIEAggregate.getString()
				long stringOffset = DWARFUtil.readOffsetByDWARFformat(reader, unit.getFormat());
				if (!debugStrings.isValid(stringOffset))
					throw new IOException("Bad string offset " + Long.toHexString(stringOffset));
				return new DWARFDeferredStringAttribute(stringOffset);
			//return new DWARFStringAttribute(debugStrings.getStringAtOffset(stringOffset));

			case DW_FORM_ref_sig8:
				throw new UnsupportedOperationException(
					"DW_FORM_ref_sig8 is currently not implemented");

			// Indirect Form
			case DW_FORM_indirect:
				DWARFForm formValue = DWARFForm.find(LEB128.decode32u(reader));
				DWARFAttributeValue value = read(reader, unit, formValue);

				return new DWARFIndirectAttribute(value, formValue);
			default:
		}
		throw new IllegalArgumentException("Unknown DWARF Form: " + form.toString());
	}

	public  DWARFAttributeFactory(DWARFProgram prog);

}