| /* ==================================================================== |
| 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 org.apache.poi.ss.formula.constant; |
| |
| import org.apache.poi.util.LittleEndianInput; |
| import org.apache.poi.util.LittleEndianOutput; |
| import org.apache.poi.util.StringUtil; |
| |
| /** |
| * To support Constant Values (2.5.7) as required by the CRN record. |
| * This class is also used for two dimensional arrays which are encoded by |
| * EXTERNALNAME (5.39) records and Array tokens. |
| */ |
| public final class ConstantValueParser { |
| // note - these (non-combinable) enum values are sparse. |
| private static final int TYPE_EMPTY = 0; |
| private static final int TYPE_NUMBER = 1; |
| private static final int TYPE_STRING = 2; |
| private static final int TYPE_BOOLEAN = 4; |
| private static final int TYPE_ERROR_CODE = 16; // TODO - update OOO document to include this value |
| |
| private static final int TRUE_ENCODING = 1; |
| private static final int FALSE_ENCODING = 0; |
| |
| // TODO - is this the best way to represent 'EMPTY'? |
| private static final Object EMPTY_REPRESENTATION = null; |
| |
| private ConstantValueParser() { |
| // no instances of this class |
| } |
| |
| public static Object[] parse(LittleEndianInput in, int nValues) { |
| if (nValues < 0) { |
| throw new IllegalArgumentException("Invalid number of values to parse: " + nValues); |
| } |
| |
| Object[] result = new Object[nValues]; |
| for (int i = 0; i < result.length; i++) { |
| result[i] = readAConstantValue(in); |
| } |
| return result; |
| } |
| |
| private static Object readAConstantValue(LittleEndianInput in) { |
| byte grbit = in.readByte(); |
| switch(grbit) { |
| case TYPE_EMPTY: |
| in.readLong(); // 8 byte 'not used' field |
| return EMPTY_REPRESENTATION; |
| case TYPE_NUMBER: |
| return in.readDouble(); |
| case TYPE_STRING: |
| return StringUtil.readUnicodeString(in); |
| case TYPE_BOOLEAN: |
| return readBoolean(in); |
| case TYPE_ERROR_CODE: |
| int errCode = in.readUShort(); |
| // next 6 bytes are unused |
| in.readUShort(); |
| in.readInt(); |
| return ErrorConstant.valueOf(errCode); |
| } |
| throw new IllegalArgumentException("Unknown grbit value (" + grbit + ")"); |
| } |
| |
| private static Object readBoolean(LittleEndianInput in) { |
| byte val = (byte)in.readLong(); // 7 bytes 'not used' |
| switch(val) { |
| case FALSE_ENCODING: |
| return Boolean.FALSE; |
| case TRUE_ENCODING: |
| return Boolean.TRUE; |
| } |
| // Don't tolerate unusual boolean encoded values (unless it becomes evident that they occur) |
| throw new IllegalArgumentException("unexpected boolean encoding (" + val + ")"); |
| } |
| |
| public static int getEncodedSize(Object[] values) { |
| // start with one byte 'type' code for each value |
| int result = values.length; |
| for (Object value : values) { |
| result += getEncodedSize(value); |
| } |
| return result; |
| } |
| |
| /** |
| * @return encoded size without the 'type' code byte |
| */ |
| private static int getEncodedSize(Object object) { |
| if(object == EMPTY_REPRESENTATION) { |
| return 8; |
| } |
| Class<?> cls = object.getClass(); |
| |
| if(cls == Boolean.class || cls == Double.class || cls == ErrorConstant.class) { |
| return 8; |
| } |
| String strVal = (String)object; |
| return StringUtil.getEncodedSize(strVal); |
| } |
| |
| public static void encode(LittleEndianOutput out, Object[] values) { |
| for (Object value : values) { |
| encodeSingleValue(out, value); |
| } |
| } |
| |
| private static void encodeSingleValue(LittleEndianOutput out, Object value) { |
| if (value == EMPTY_REPRESENTATION) { |
| out.writeByte(TYPE_EMPTY); |
| out.writeLong(0L); |
| return; |
| } |
| if (value instanceof Boolean) { |
| Boolean bVal = ((Boolean)value); |
| out.writeByte(TYPE_BOOLEAN); |
| long longVal = bVal ? 1L : 0L; |
| out.writeLong(longVal); |
| return; |
| } |
| if (value instanceof Double) { |
| Double dVal = (Double) value; |
| out.writeByte(TYPE_NUMBER); |
| out.writeDouble(dVal); |
| return; |
| } |
| if (value instanceof String) { |
| String val = (String) value; |
| out.writeByte(TYPE_STRING); |
| StringUtil.writeUnicodeString(out, val); |
| return; |
| } |
| if (value instanceof ErrorConstant) { |
| ErrorConstant ecVal = (ErrorConstant) value; |
| out.writeByte(TYPE_ERROR_CODE); |
| long longVal = ecVal.getErrorCode(); |
| out.writeLong(longVal); |
| return; |
| } |
| |
| throw new IllegalStateException("Unexpected value type (" + value.getClass().getName() + "'"); |
| } |
| } |