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.util.ArrayList;
020:
021: import org.apache.poi.hdgf.chunks.ChunkFactory.CommandDefinition;
022: import org.apache.poi.util.LittleEndian;
023: import org.apache.poi.util.POILogFactory;
024: import org.apache.poi.util.POILogger;
025: import org.apache.poi.util.StringUtil;
026:
027: /**
028: * Base of all chunks, which hold data, flags etc
029: */
030: public class Chunk {
031: /**
032: * The contents of the chunk, excluding the header,
033: * trailer and separator
034: */
035: private byte[] contents;
036: private ChunkHeader header;
037: /** May be null */
038: private ChunkTrailer trailer;
039: /** May be null */
040: private ChunkSeparator separator;
041: /** The possible different commands we can hold */
042: protected CommandDefinition[] commandDefinitions;
043: /** The command+value pairs we hold */
044: private Command[] commands;
045: /** The blocks (if any) we hold */
046: //private Block[] blocks
047: /** The name of the chunk, as found from the commandDefinitions */
048: private String name;
049:
050: /** For logging warnings about the structure of the file */
051: private POILogger logger = POILogFactory.getLogger(Chunk.class);
052:
053: public Chunk(ChunkHeader header, ChunkTrailer trailer,
054: ChunkSeparator separator, byte[] contents) {
055: this .header = header;
056: this .trailer = trailer;
057: this .separator = separator;
058: this .contents = contents;
059: }
060:
061: public byte[] _getContents() {
062: return contents;
063: }
064:
065: public ChunkHeader getHeader() {
066: return header;
067: }
068:
069: /** Gets the separator between this chunk and the next, if it exists */
070: public ChunkSeparator getSeparator() {
071: return separator;
072: }
073:
074: /** Gets the trailer for this chunk, if it exists */
075: public ChunkTrailer getTrailer() {
076: return trailer;
077: }
078:
079: /**
080: * Gets the command definitions, which define and describe much
081: * of the data held by the chunk.
082: */
083: public CommandDefinition[] getCommandDefinitions() {
084: return commandDefinitions;
085: }
086:
087: public Command[] getCommands() {
088: return commands;
089: }
090:
091: /**
092: * Get the name of the chunk, as found from the CommandDefinitions
093: */
094: public String getName() {
095: return name;
096: }
097:
098: /**
099: * Returns the size of the chunk, including any
100: * headers, trailers and separators.
101: */
102: public int getOnDiskSize() {
103: int size = header.getSizeInBytes() + contents.length;
104: if (trailer != null) {
105: size += trailer.trailerData.length;
106: }
107: if (separator != null) {
108: size += separator.separatorData.length;
109: }
110: return size;
111: }
112:
113: /**
114: * Uses our CommandDefinitions to process the commands
115: * our chunk type has, and figure out the
116: * values for them.
117: */
118: protected void processCommands() {
119: if (commandDefinitions == null) {
120: throw new IllegalStateException(
121: "You must supply the command definitions before calling processCommands!");
122: }
123:
124: // Loop over the definitions, building the commands
125: // and getting their values
126: ArrayList commands = new ArrayList();
127: for (int i = 0; i < commandDefinitions.length; i++) {
128: int type = commandDefinitions[i].getType();
129: int offset = commandDefinitions[i].getOffset();
130:
131: // Handle virtual commands
132: if (type == 10) {
133: name = commandDefinitions[i].getName();
134: continue;
135: } else if (type == 18) {
136: continue;
137: }
138:
139: // Build the appropriate command for the type
140: Command command;
141: if (type == 11 || type == 21) {
142: command = new BlockOffsetCommand(commandDefinitions[i]);
143: } else {
144: command = new Command(commandDefinitions[i]);
145: }
146:
147: // Bizarely, many of the offsets are from the start of the
148: // header, not from the start of the chunk body
149: switch (type) {
150: case 0:
151: case 1:
152: case 2:
153: case 3:
154: case 4:
155: case 5:
156: case 6:
157: case 7:
158: case 11:
159: case 21:
160: case 12:
161: case 16:
162: case 17:
163: case 18:
164: case 28:
165: case 29:
166: // Offset is from start of chunk
167: break;
168: default:
169: // Offset is from start of header!
170: if (offset >= 19) {
171: offset -= 19;
172: }
173: }
174:
175: // Check we seem to have enough data
176: if (offset >= contents.length) {
177: logger.log(POILogger.WARN, "Command offset " + offset
178: + " past end of data at " + contents.length);
179: continue;
180: }
181:
182: // Process
183: switch (type) {
184: // Types 0->7 = a flat at bit 0->7
185: case 0:
186: case 1:
187: case 2:
188: case 3:
189: case 4:
190: case 5:
191: case 6:
192: case 7:
193: int val = contents[offset] & (1 << type);
194: command.value = new Boolean((val > 0));
195: break;
196: case 8:
197: command.value = new Byte(contents[offset]);
198: break;
199: case 9:
200: command.value = new Double(LittleEndian.getDouble(
201: contents, offset));
202: break;
203: case 12:
204: // A Little Endian String
205: // Starts 8 bytes into the data segment
206: // Ends at end of data, or 00 00
207: int startsAt = 8;
208: int endsAt = startsAt;
209: for (int j = startsAt; j < contents.length - 1
210: && endsAt == startsAt; j++) {
211: if (contents[j] == 0 && contents[j + 1] == 0) {
212: endsAt = j;
213: }
214: }
215: if (endsAt == startsAt) {
216: endsAt = contents.length;
217: }
218:
219: int strLen = (endsAt - startsAt) / 2;
220: command.value = StringUtil.getFromUnicodeLE(contents,
221: startsAt, strLen);
222: break;
223: case 25:
224: command.value = new Short(LittleEndian.getShort(
225: contents, offset));
226: break;
227: case 26:
228: command.value = new Integer(LittleEndian.getInt(
229: contents, offset));
230: break;
231:
232: // Types 11 and 21 hold the offset to the blocks
233: case 11:
234: case 21:
235: if (offset < contents.length - 3) {
236: int bOffset = (int) LittleEndian.getUInt(contents,
237: offset);
238: BlockOffsetCommand bcmd = (BlockOffsetCommand) command;
239: bcmd.setOffset(bOffset);
240: }
241: break;
242:
243: default:
244: logger.log(POILogger.INFO, "Command of type " + type
245: + " not processed!");
246: }
247:
248: // Add to the array
249: commands.add(command);
250: }
251:
252: // Save the commands we liked the look of
253: this .commands = (Command[]) commands
254: .toArray(new Command[commands.size()]);
255:
256: // Now build up the blocks, if we had a command that tells
257: // us where a block is
258: }
259:
260: /**
261: * A command in the visio file. In order to make things fun,
262: * all the chunk actually stores is the value of the command.
263: * You have to have your own lookup table to figure out what
264: * the commands are based on the chunk type.
265: */
266: public static class Command {
267: protected Object value;
268: private CommandDefinition definition;
269:
270: private Command(CommandDefinition definition, Object value) {
271: this .definition = definition;
272: this .value = value;
273: }
274:
275: private Command(CommandDefinition definition) {
276: this (definition, null);
277: }
278:
279: public CommandDefinition getDefinition() {
280: return definition;
281: }
282:
283: public Object getValue() {
284: return value;
285: }
286: }
287:
288: /**
289: * A special kind of command that is an artificat of how we
290: * process CommandDefinitions, and so doesn't actually exist
291: * in the chunk
292: */
293: // public static class VirtualCommand extends Command {
294: // private VirtualCommand(CommandDefinition definition) {
295: // super(definition);
296: // }
297: // }
298: /**
299: * A special kind of command that holds the offset to
300: * a block
301: */
302: public static class BlockOffsetCommand extends Command {
303: private int offset;
304:
305: private BlockOffsetCommand(CommandDefinition definition) {
306: super (definition, null);
307: }
308:
309: private void setOffset(int offset) {
310: this .offset = offset;
311: value = new Integer(offset);
312: }
313: }
314: }
|