| /* ==================================================================== |
| 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.ddf; |
| |
| import java.lang.reflect.Constructor; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.poi.util.LittleEndian; |
| |
| /** |
| * Generates escher records when provided the byte array containing those records. |
| * |
| * @see EscherRecordFactory |
| */ |
| public class DefaultEscherRecordFactory implements EscherRecordFactory { |
| private static Class<?>[] escherRecordClasses = { EscherBSERecord.class, |
| EscherOptRecord.class, EscherTertiaryOptRecord.class, |
| EscherClientAnchorRecord.class, EscherDgRecord.class, |
| EscherSpgrRecord.class, EscherSpRecord.class, |
| EscherClientDataRecord.class, EscherDggRecord.class, |
| EscherSplitMenuColorsRecord.class, EscherChildAnchorRecord.class, |
| EscherTextboxRecord.class }; |
| private static Map<Short, Constructor<? extends EscherRecord>> recordsMap = recordsToMap( escherRecordClasses ); |
| |
| /** |
| * Creates an instance of the escher record factory |
| */ |
| public DefaultEscherRecordFactory() { |
| // no instance initialisation |
| } |
| |
| @Override |
| public EscherRecord createRecord(byte[] data, int offset) { |
| short options = LittleEndian.getShort( data, offset ); |
| short recordId = LittleEndian.getShort( data, offset + 2 ); |
| // int remainingBytes = LittleEndian.getInt( data, offset + 4 ); |
| |
| // Options of 0x000F means container record |
| // However, EscherTextboxRecord are containers of records for the |
| // host application, not of other Escher records, so treat them |
| // differently |
| if (isContainer(options, recordId)) { |
| EscherContainerRecord r = new EscherContainerRecord(); |
| r.setRecordId( recordId ); |
| r.setOptions( options ); |
| return r; |
| } |
| |
| if (recordId >= EscherBlipRecord.RECORD_ID_START |
| && recordId <= EscherBlipRecord.RECORD_ID_END) { |
| EscherBlipRecord r; |
| if (recordId == EscherBitmapBlip.RECORD_ID_DIB || |
| recordId == EscherBitmapBlip.RECORD_ID_JPEG || |
| recordId == EscherBitmapBlip.RECORD_ID_PNG) |
| { |
| r = new EscherBitmapBlip(); |
| } |
| else if (recordId == EscherMetafileBlip.RECORD_ID_EMF || |
| recordId == EscherMetafileBlip.RECORD_ID_WMF || |
| recordId == EscherMetafileBlip.RECORD_ID_PICT) |
| { |
| r = new EscherMetafileBlip(); |
| } else { |
| r = new EscherBlipRecord(); |
| } |
| r.setRecordId( recordId ); |
| r.setOptions( options ); |
| return r; |
| } |
| |
| Constructor<? extends EscherRecord> recordConstructor = recordsMap.get(Short.valueOf(recordId)); |
| final EscherRecord escherRecord; |
| if (recordConstructor == null) { |
| return new UnknownEscherRecord(); |
| } |
| try { |
| escherRecord = recordConstructor.newInstance(); |
| } catch (Exception e) { |
| return new UnknownEscherRecord(); |
| } |
| escherRecord.setRecordId(recordId); |
| escherRecord.setOptions(options); |
| return escherRecord; |
| } |
| |
| /** |
| * Converts from a list of classes into a map that contains the record id as the key and |
| * the Constructor in the value part of the map. It does this by using reflection to look up |
| * the RECORD_ID field then using reflection again to find a reference to the constructor. |
| * |
| * @param recClasses The records to convert |
| * @return The map containing the id/constructor pairs. |
| */ |
| protected static Map<Short, Constructor<? extends EscherRecord>> recordsToMap(Class<?>[] recClasses) { |
| Map<Short, Constructor<? extends EscherRecord>> result = new HashMap<Short, Constructor<? extends EscherRecord>>(); |
| final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0]; |
| |
| for (Class<?> recClass : recClasses) { |
| @SuppressWarnings("unchecked") |
| Class<? extends EscherRecord> recCls = (Class<? extends EscherRecord>) recClass; |
| short sid; |
| try { |
| sid = recCls.getField("RECORD_ID").getShort(null); |
| } catch (IllegalArgumentException e) { |
| throw new RuntimeException(e); |
| } catch (IllegalAccessException e) { |
| throw new RuntimeException(e); |
| } catch (NoSuchFieldException e) { |
| throw new RuntimeException(e); |
| } |
| Constructor<? extends EscherRecord> constructor; |
| try { |
| constructor = recCls.getConstructor(EMPTY_CLASS_ARRAY); |
| } catch (NoSuchMethodException e) { |
| throw new RuntimeException(e); |
| } |
| result.put(Short.valueOf(sid), constructor); |
| } |
| return result; |
| } |
| |
| public static boolean isContainer(short options, short recordId){ |
| if(recordId >= EscherContainerRecord.DGG_CONTAINER && recordId |
| <= EscherContainerRecord.SOLVER_CONTAINER){ |
| return true; |
| } else { |
| if (recordId == EscherTextboxRecord.RECORD_ID) { |
| return false; |
| } else { |
| return ( options & (short) 0x000F ) == (short) 0x000F; |
| } |
| } |
| } |
| } |