001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017: package org.apache.poi.hdgf.chunks;
018:
019: import java.io.BufferedReader;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.InputStreamReader;
023: import java.util.ArrayList;
024: import java.util.Hashtable;
025: import java.util.StringTokenizer;
026:
027: import org.apache.poi.util.POILogFactory;
028: import org.apache.poi.util.POILogger;
029:
030: /**
031: * Factor class to create the appropriate chunks, which
032: * needs the version of the file to process the chunk header
033: * and trailer areas.
034: * Makes use of chunks_parse_cmds.tbl from vsdump to be able
035: * to process the chunk value area
036: */
037: public class ChunkFactory {
038: /** The version of the currently open document */
039: private int version;
040: /**
041: * Key is a Chunk's type, value is an array of its CommandDefinitions
042: */
043: private Hashtable chunkCommandDefinitions = new Hashtable();
044: /** What the name is of the chunk table */
045: private static String chunkTableName = "/org/apache/poi/hdgf/chunks/chunks_parse_cmds.tbl";
046:
047: /** For logging problems we spot with the file */
048: private POILogger logger = POILogFactory
049: .getLogger(ChunkFactory.class);
050:
051: public ChunkFactory(int version) throws IOException {
052: this .version = version;
053:
054: processChunkParseCommands();
055: }
056:
057: /**
058: * Open chunks_parse_cmds.tbl and process it, to get the definitions
059: * of all the different possible chunk commands.
060: */
061: private void processChunkParseCommands() throws IOException {
062: String line;
063: InputStream cpd = ChunkFactory.class
064: .getResourceAsStream(chunkTableName);
065: BufferedReader inp = new BufferedReader(new InputStreamReader(
066: cpd));
067: while ((line = inp.readLine()) != null) {
068: if (line.startsWith("#"))
069: continue;
070: if (line.startsWith(" "))
071: continue;
072: if (line.startsWith("\t"))
073: continue;
074: if (line.length() == 0)
075: continue;
076:
077: // Start xxx
078: if (!line.startsWith("start")) {
079: throw new IllegalStateException(
080: "Expecting start xxx, found " + line);
081: }
082: int chunkType = Integer.parseInt(line.substring(6));
083: ArrayList defsL = new ArrayList();
084:
085: // Data entries
086: while (!(line = inp.readLine()).startsWith("end")) {
087: StringTokenizer st = new StringTokenizer(line, " ");
088: int defType = Integer.parseInt(st.nextToken());
089: int offset = Integer.parseInt(st.nextToken());
090: String name = st.nextToken("\uffff").substring(1);
091:
092: CommandDefinition def = new CommandDefinition(defType,
093: offset, name);
094: defsL.add(def);
095: }
096:
097: CommandDefinition[] defs = (CommandDefinition[]) defsL
098: .toArray(new CommandDefinition[defsL.size()]);
099:
100: // Add to the hashtable
101: chunkCommandDefinitions.put(new Integer(chunkType), defs);
102: }
103: inp.close();
104: cpd.close();
105: }
106:
107: public int getVersion() {
108: return version;
109: }
110:
111: /**
112: * Creates the appropriate chunk at the given location.
113: * @param data
114: * @param offset
115: */
116: public Chunk createChunk(byte[] data, int offset) {
117: // Create the header
118: ChunkHeader header = ChunkHeader.createChunkHeader(version,
119: data, offset);
120: // Sanity check
121: if (header.length < 0) {
122: throw new IllegalArgumentException(
123: "Found a chunk with a negative length, which isn't allowed");
124: }
125:
126: // How far up to look
127: int endOfDataPos = offset + header.getLength()
128: + header.getSizeInBytes();
129:
130: // Check we have enough data, and tweak the header size
131: // as required
132: if (endOfDataPos > data.length) {
133: logger
134: .log(
135: POILogger.WARN,
136: "Header called for "
137: + header.getLength()
138: + " bytes, but that would take us passed the end of the data!");
139:
140: endOfDataPos = data.length;
141: header.length = data.length - offset
142: - header.getSizeInBytes();
143:
144: if (header.hasTrailer()) {
145: header.length -= 8;
146: endOfDataPos -= 8;
147: }
148: if (header.hasSeparator()) {
149: header.length -= 4;
150: endOfDataPos -= 4;
151: }
152: }
153:
154: // Create the trailer and separator, if required
155: ChunkTrailer trailer = null;
156: ChunkSeparator separator = null;
157: if (header.hasTrailer()) {
158: if (endOfDataPos <= data.length - 8) {
159: trailer = new ChunkTrailer(data, endOfDataPos);
160: endOfDataPos += 8;
161: } else {
162: System.err
163: .println("Header claims a length to "
164: + endOfDataPos
165: + " there's then no space for the trailer in the data ("
166: + data.length + ")");
167: }
168: }
169: if (header.hasSeparator()) {
170: if (endOfDataPos <= data.length - 4) {
171: separator = new ChunkSeparator(data, endOfDataPos);
172: } else {
173: System.err
174: .println("Header claims a length to "
175: + endOfDataPos
176: + " there's then no space for the separator in the data ("
177: + data.length + ")");
178: }
179: }
180:
181: // Now, create the chunk
182: byte[] contents = new byte[header.getLength()];
183: System.arraycopy(data, offset + header.getSizeInBytes(),
184: contents, 0, contents.length);
185: Chunk chunk = new Chunk(header, trailer, separator, contents);
186:
187: // Feed in the stuff from chunks_parse_cmds.tbl
188: CommandDefinition[] defs = (CommandDefinition[]) chunkCommandDefinitions
189: .get(new Integer(header.getType()));
190: if (defs == null)
191: defs = new CommandDefinition[0];
192: chunk.commandDefinitions = defs;
193:
194: // Now get the chunk to process its commands
195: chunk.processCommands();
196:
197: // All done
198: return chunk;
199: }
200:
201: /**
202: * The definition of a Command, which a chunk may hold.
203: * The Command holds the value, this describes it.
204: */
205: public class CommandDefinition {
206: private int type;
207: private int offset;
208: private String name;
209:
210: public CommandDefinition(int type, int offset, String name) {
211: this .type = type;
212: this .offset = offset;
213: this .name = name;
214: }
215:
216: public String getName() {
217: return name;
218: }
219:
220: public int getOffset() {
221: return offset;
222: }
223:
224: public int getType() {
225: return type;
226: }
227: }
228: }
|