summaryrefslogtreecommitdiffstats
path: root/documentation/advanced/advanced-debug.asciidoc
blob: 3bbd6eb931efd52adaca55fe5fcd69b8266883cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
---
title: Debug Mode and Window
order: 3
layout: page
---

[[advanced.debug]]
= Debug Mode and Window

Vaadin applications can be run in two modes: __debug mode__ and __production
mode__. The debug mode, which is on by default, enables a number of built-in
debug features for Vaadin developers:

* Debug Window
* Display debug information in the Debug Window and server console
* On-the-fly compilation of Sass themes
* Timings of server calls for Vaadin TestBench

It is recommended to always deploy production applications in production mode
for security reasons.

[[advanced.debug.mode]]
== Enabling the Debug Mode

The debug mode is enabled and production mode disabled by default in the UI
templates created with the Eclipse plugin or the Maven archetypes. Some
archetypes have a separate module and profile for producing a production mode
application. The debug mode can be enabled by giving a
[parameter]#productionMode=false# parameter to the Vaadin servlet
configuration:

[subs="normal"]
----
@VaadinServletConfiguration(
            productionMode = **false**,
            ui = **MyprojectUI.class**)
----
Or with a context parameter in the [filename]#web.xml# deployment descriptor:

[subs="normal"]
----
<context-param>
  <description>Vaadin production mode</description>
  <param-name>productionMode</param-name>
  <param-value>**false**</param-value>
</context-param>
----
Enabling the production mode disables the debug features, thereby preventing
users from easily inspecting the inner workings of the application from the
browser.


[[advanced.debug.open]]
== Opening the Debug Window

Running an application in the debug mode enables the client-side Debug Window in
the browser. You can open the Debug Window by adding " ?debug" parameter to the
URL of the UI, for example, http://localhost:8080/myapp/?debug. The Debug Window
has buttons for controlling the debugging features and a scrollable log of debug
messages.

[[]]
.Debug Window
image::img/debug-window-annotated-hi.png[]

The functionalities are described in detail in the subsequent sections. You can
move the window by dragging it from the title bar and resize it from the
corners. The [guibutton]#Minimize# button minimizes the debug window in the
corner of the browser window, and the [guibutton]#Close# button closes it.

If you use the Firebug plugin for Firefox or the Developer Tools console in
Chrome, the log messages will also be printed to the Firebug console. In such a
case, you may want to enable client-side debugging without showing the Debug
Window with " ?debug=quiet" in the URL. In the quiet debug mode, log messages
will only be printed to the console of the browser debugger.


[[advanced.debug.log]]
== Debug Message Log

The debug message log displays client-side debug messages, with time counter in
milliseconds. The control buttons allow you to clear the log, reset the timer,
and lock scrolling.

[[]]
.Debug Message Log
image::img/debug-log-hi.png[]

[[advanced.debug.log.custom]]
=== Logging to Debug Window

You can take advantage of the debug mode when developing client-side components,
by using the standard Java [classname]#Logger# to write messages to the log. The
messages will be written to the debug window and Firebug console. No messages
are written if the debug window is not open or if the application is running in
production mode.



[[advanced.debug.info]]
== General Information

The [guilabel]#General information about the application(s)# tab displays
various information about the UI, such as version numbers of the client and
servlet engine, and the theme. If they do not match, you may need to compile the
widget set or theme.

[[]]
.General Information
image::img/debug-info.png[]


[[advanced.debug.hierarchy]]
== Inspecting Component Hierarchy

The [guilabel]#Component Hierarchy# tab has several sub-modes that allow
debugging the component tree in various ways.

[[advanced.debug.hierarchy.tree]]
=== Connector Hierarchy Tree

The [guibutton]#Show the connector hierarchy tree# button displays the
client-side connector hierarchy. As explained in
<<dummy/../../../framework/gwt/gwt-overview.asciidoc#gwt.overview,"Integrating
with the Server-Side">>, client-side widgets are managed by connectors that
handle communication with the server-side component counterparts. The connector
hierarchy therefore corresponds with the server-side component tree, but the
client-side widget tree and HTML DOM tree have more complexity.

[[]]
.Connector Hierarchy Tree
image::img/debug-hierarchy-tree.png[]

Clicking on a connector highlights the widget in the UI.


[[advanced.debug.hierarchy.inspect]]
=== Inspecting a Component

The [guibutton]#Select a component in the page to inspect it# button lets you
select a component in the UI by clicking it and display its client-side
properties.

To view the HTML structure and CSS styles in more detail, you can use Firebug in
Firefox, or the Developer Tools in Chrome, as described in
<<dummy/../../../framework/installing/installing-other#installing.other.firefox,"Firefox and Firebug">>. Firefox also has a built-in feature for inspecting HTML and CSS.


[[advanced.debug.hierarchy.analyze]]
=== Analyzing Layout Problems

The [guilabel]#Check layouts for potential problems# button analyzes the
currently visible UI and makes a report of possible layout related problems. All
detected layout problems are displayed in the log and also printed to the
console.

[[figure.advanced.debug.hierarchy.analyze]]
.Debug window showing the result of layout analysis.
image::img/debug-window-analyze-layouts.png[]

Clicking on a reported problem highlights the component with the problem in the
UI.

The most common layout problem is caused by placing a component that has a
relative size inside a container (layout) that has undefined size in the
particular direction (height or width). For example, adding a
[classname]#Button# with 100% width inside a [classname]#VerticalLayout# with
undefined width. In such a case, the error would look as shown in
<<figure.advanced.debug.hierarchy.analyze>>.

[classname]#CustomLayout# components can not be analyzed in the same way as
other layouts. For custom layouts, the button analyzes all contained
relative-sized components and checks if any relative dimension is calculated to
zero so that the component will be invisible. The error log will display a
warning for each of these invisible components. It would not be meaningful to
emphasize the component itself as it is not visible, so when you select such an
error, the parent layout of the component is emphasized if possible.


[[advanced.debug.hierarchy.used]]
=== Displaying Used Connectors

The last button, [guibutton]#Show used connectors and how to optimize widget
set#, displays a list of all currently visible connectors. It also generates a
connector bundle loader factory, which you can use to optimize the widget set so
that it only contains the widgets actually used in the UI. Note, however, that
it only lists the connectors visible in the current UI state, and you usually
have more connectors than that.



[[advanced.debug.communication]]
== Communication Log

The [guilabel]#Communication# tab displays all server requests. You can unfold
the requests to view details, such as the connectors involved. Clicking on a
connector highlights the corresponding element in the UI.

You can use Firebug or Developer Tools in Firefox or Chrome, respectively, to
get more detailed information about the requests and responses.


[[advanced.debug.devmodes]]
== Debug Modes

The [guilabel]#Menu# tab in the window opens a sub-menu to select various
settings. Here you can also launch the GWT SuperDevMode, as described in
<<dummy/../../../framework/clientside/clientside-debugging#clientside.debugging,"Debugging
Client-Side Code">>.
> 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
/*
 *  ====================================================================
 *  The Apache Software License, Version 1.1
 *
 *  Copyright (c) 2000 The Apache Software Foundation.  All rights
 *  reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in
 *  the documentation and/or other materials provided with the
 *  distribution.
 *
 *  3. The end-user documentation included with the redistribution,
 *  if any, must include the following acknowledgment:
 *  "This product includes software developed by the
 *  Apache Software Foundation (http://www.apache.org/)."
 *  Alternately, this acknowledgment may appear in the software itself,
 *  if and wherever such third-party acknowledgments normally appear.
 *
 *  4. The names "Apache" and "Apache Software Foundation" must
 *  not be used to endorse or promote products derived from this
 *  software without prior written permission. For written
 *  permission, please contact apache@apache.org.
 *
 *  5. Products derived from this software may not be called "Apache",
 *  nor may "Apache" appear in their name, without prior written
 *  permission of the Apache Software Foundation.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 *  ====================================================================
 *
 *  This software consists of voluntary contributions made by many
 *  individuals on behalf of the Apache Software Foundation.  For more
 *  information on the Apache Software Foundation, please see
 *  <http://www.apache.org/>.
 */
package org.apache.poi.hpsf;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.poi.hpsf.wellknown.PropertyIDMap;
import org.apache.poi.util.LittleEndian;

/**
 * <p>Adds writing capability to the {@link Section} class.</p>
 *
 * <p>Please be aware that this class' functionality will be merged into the
 * {@link Section} class at a later time, so the API will change.</p>
 *
 * @version $Id$
 * @since 2002-02-20
 */
public class MutableSection extends Section
{
    /**
     * <p>If the "dirty" flag is true, the section's size must be
     * (re-)calculated before the section is written.</p>
     */
    private boolean dirty = true;



    /**
     * <p>List to assemble the properties. Unfortunately a wrong
     * decision has been taken when specifying the "properties" field
     * as an Property[]. It should have been a {@link java.util.List}.</p>
     */
    private List preprops;



    /**
     * <p>Contains the bytes making out the section. This byte array is
     * established when the section's size is calculated and can be reused
     * later. It is valid only if the "dirty" flag is false.</p>
     */
    private byte[] sectionBytes;



    /**
     * <p>Creates an empty mutable section.</p>
     */
    public MutableSection()
    {
        dirty = true;
        formatID = null;
        offset = -1;
        preprops = new LinkedList();
    }



    /**
     * <p>Constructs a <code>MutableSection</code> by doing a deep copy of an 
     * existing <code>Section</code>. All nested <code>Property</code> 
     * instances, will be their mutable counterparts in the new
     * <code>MutableSection</code>.</p>
     * 
     * @param s The section set to copy
     */
    public MutableSection(final Section s)
    {
        setFormatID(s.getFormatID());
        final Property[] pa = s.getProperties();
        final MutableProperty[] mpa = new MutableProperty[pa.length];
        for (int i = 0; i < pa.length; i++)
            mpa[i] = new MutableProperty(pa[i]);
        setProperties(mpa);
        setDictionary(s.getDictionary());
    }



    /**
     * <p>Sets the section's format ID.</p>
     *
     * @param formatID The section's format ID
     *
     * @see #setFormatID(byte[])
     * @see #getFormatID
     */
    public void setFormatID(final ClassID formatID)
    {
        this.formatID = formatID;
    }



    /**
     * <p>Sets the section's format ID.</p>
     *
     * @param formatID The section's format ID as a byte array. It components
     * are in big-endian format.
     *
     * @see #setFormatID(ClassID)
     * @see #getFormatID
     */
    public void setFormatID(final byte[] formatID)
    {
        setFormatID(new ClassID(formatID, 0));
    }



    /**
     * <p>Sets this section's properties. Any former values are overwritten.</p>
     *
     * @param properties This section's new properties.
     */
    public void setProperties(final Property[] properties)
    {
        this.properties = properties;
        preprops = new LinkedList();
        for (int i = 0; i < properties.length; i++)
            preprops.add(properties[i]);
        dirty = true;
        propertyCount = properties.length;
    }



    /**
     * <p>Sets the value of the property with the specified ID. If a
     * property with this ID is not yet present in the section, it
     * will be added. An already present property with the specified
     * ID will be overwritten.</p>
     *
     * @param id The property's ID
     * @param value The property's value. It will be written as a Unicode
     * string.
     *
     * @see #setProperty(int, long, Object)
     * @see #getProperty
     */
    public void setProperty(final int id, final String value)
    {
        setProperty(id, Variant.VT_LPWSTR, value);
        dirty = true;
    }



    /**
     * <p>Sets the value and the variant type of the property with the
     * specified ID. If a property with this ID is not yet present in
     * the section, it will be added. An already present property with
     * the specified ID will be overwritten. A default mapping will be
     * used to choose the property's type.</p>
     *
     * @param id The property's ID.
     * @param variantType The property's variant type.
     * @param value The property's value.
     *
     * @see #setProperty(int, String)
     * @see #getProperty
     * @see Variant
     */
    public void setProperty(final int id, final long variantType,
                            final Object value)
    {
        final MutableProperty p = new MutableProperty();
        p.setID(id);
        p.setType(variantType);
        p.setValue(value);
        setProperty(p);
        dirty = true;
    }



    /**
     * <p>Sets a property. If a property with the same ID is not yet present in
     * the section, the property will be added to the section. If there is
     * already a property with the same ID present in the section, it will be
     * overwritten.</p>
     *
     * @param p The property to be added to the section
     *
     * @see #setProperty(int, long, Object)
     * @see #setProperty(int, String)
     * @see #getProperty
     * @see Variant
     */
    public void setProperty(final Property p)
    {
        final long id = p.getID();
        removeProperty(id);
        preprops.add(p);
        dirty = true;
        propertyCount = preprops.size();
    }



    /**
     * <p>Removes a property.</p>
     *
     * @param id The ID of the property to be removed
     */
    public void removeProperty(final long id)
    {
        for (final Iterator i = preprops.iterator(); i.hasNext();)
            if (((Property) i.next()).getID() == id)
            {
                i.remove();
                break;
            }
        dirty = true;
        propertyCount = preprops.size();
    }



    /**
     * <p>Sets the value of the boolean property with the specified
     * ID.</p>
     *
     * @param id The property's ID
     * @param value The property's value
     *
     * @see #setProperty(int, long, Object)
     * @see #getProperty
     * @see Variant
     */
    protected void setPropertyBooleanValue(final int id, final boolean value)
    {
        setProperty(id, (long) Variant.VT_BOOL, new Boolean(value));
    }



    /**
     * <p>Returns the section's size.</p>
     *
     * @return the section's size.
     */
    public int getSize()
    {
        if (dirty)
        {
            try
            {
                size = calcSize();
                dirty = false;
            }
            catch (HPSFRuntimeException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw new HPSFRuntimeException(ex);
            }
        }
        return size;
    }



    /**
     * <p>Calculates the section's size. It is the sum of the lengths of the
     * section's header (8), the properties list (16 times the number of
     * properties) and the properties themselves.</p>
     *
     * @return the section's length in bytes.
     */
    private int calcSize() throws WritingNotSupportedException, IOException 
    {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        write(out);
        out.close();
        sectionBytes = out.toByteArray();
        return sectionBytes.length;
    }



    /**
     * <p>Writes this section into an output stream.</p>
     * 
     * <p>Internally this is done by writing into three byte array output
     * streams: one for the properties, one for the property list and one for
     * the section as such. The two former are appended to the latter when they
     * have received all their data.</p>
     *
     * @param out The stream to write into.
     *
     * @return The number of bytes written, i.e. the section's size.
     * @exception IOException if an I/O error occurs
     * @exception WritingNotSupportedException if HPSF does not yet support
     * writing a property's variant type.
     */
    public int write(final OutputStream out)
        throws WritingNotSupportedException, IOException
    {
        /* Check whether we have already generated the bytes making out the
         * section. */
        if (!dirty && sectionBytes != null)
        {
            out.write(sectionBytes);
            return sectionBytes.length;
        }

        /* The properties are written to this stream. */
        final ByteArrayOutputStream propertyStream =
            new ByteArrayOutputStream();

        /* The property list is established here. After each property that has
         * been written to "propertyStream", a property list entry is written to
         * "propertyListStream". */
        final ByteArrayOutputStream propertyListStream =
            new ByteArrayOutputStream();
 
        /* Maintain the current position in the list. */
        int position = 0;

        /* Increase the position variable by the size of the property list so
         * that it points behind the property list and to the beginning of the
         * properties themselves. */
        position += 2 * LittleEndian.INT_SIZE +
                    getPropertyCount() * 2 * LittleEndian.INT_SIZE;

        /* Writing the section's dictionary it tricky. If there is a dictionary
         * (property 0) the codepage property (property 1) has to be set, too.
         * Since HPSF supports Unicode only, the codepage must be 1200. */
        if (getProperty(PropertyIDMap.PID_DICTIONARY) != null)
        {
            final Object p1 = getProperty(PropertyIDMap.PID_CODEPAGE);
            if (p1 != null)
            {
                if (!(p1 instanceof Integer))
                    throw new IllegalPropertySetDataException
                        ("The codepage property (ID = 1) must be an " +
                         "Integer object.");
                else if (((Integer) p1).intValue() != Property.CP_UNICODE)
                    throw new IllegalPropertySetDataException
                        ("The codepage property (ID = 1) must be " +
                         "1200 (Unicode).");
            }
            else
                throw new IllegalPropertySetDataException
                    ("The codepage property (ID = 1) must be set.");
        }

        /* Write the properties and the property list into their respective
         * streams: */
        for (final Iterator i = preprops.iterator(); i.hasNext();)
        {
            final MutableProperty p = (MutableProperty) i.next();
            final long id = p.getID();
            
            /* Write the property list entry. */
            TypeWriter.writeUIntToStream(propertyListStream, p.getID());
            TypeWriter.writeUIntToStream(propertyListStream, position);

            /* If the property ID is not equal 0 we write the property and all
             * is fine. However, if it equals 0 we have to write the section's
             * dictionary which has an implicit type only and an explicit
             * value. */
            if (id != 0)
                /* Write the property and update the position to the next
                 * property. */
                position += p.write(propertyStream, getCodepage());
            else
            {
                final int codepage = getCodepage();
                if (codepage == -1)
                    throw new IllegalPropertySetDataException
                        ("Codepage (property 1) is undefined.");
                position += writeDictionary(propertyStream, dictionary);
            }
        }
        propertyStream.close();
        propertyListStream.close();

        /* Write the section: */
        byte[] pb1 = propertyListStream.toByteArray();
        byte[] pb2 = propertyStream.toByteArray();
        
        /* Write the section's length: */
        TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 +
                                      pb1.length + pb2.length);
        
        /* Write the section's number of properties: */
        TypeWriter.writeToStream(out, getPropertyCount());
        
        /* Write the property list: */
        out.write(pb1);
        
        /* Write the properties: */
        out.write(pb2);

        int streamLength = LittleEndian.INT_SIZE * 2 + pb1.length + pb2.length;
        return streamLength;
    }



    /**
     * <p>Writes the section's dictionary.</p>
     *
     * @param out The output stream to write to.
     * @param dictionary The dictionary.
     * @return The number of bytes written
     * @exception IOException if an I/O exception occurs.
     */
    private static int writeDictionary(final OutputStream out,
                                       final Map dictionary)
        throws IOException
    {
        int length = 0;
        length += TypeWriter.writeUIntToStream(out, dictionary.size());
        for (final Iterator i = dictionary.keySet().iterator(); i.hasNext();)
        {
            final Long key = (Long) i.next();
            final String value = (String) dictionary.get(key);
            int sLength = value.length() + 1;
            if (sLength % 2 == 1)
                sLength++;
            length += TypeWriter.writeUIntToStream(out, key.longValue());
            length += TypeWriter.writeUIntToStream(out, sLength);
            final char[] ca = value.toCharArray();
            for (int j = 0; j < ca.length; j++)
            {
                int high = (ca[j] & 0x0ff00) >> 8;
                int low  = (ca[j] & 0x000ff);
                out.write(low);
                out.write(high);
                length += 2;
                sLength--;
            }
            while (sLength > 0)
            {
                out.write(0x00);
                out.write(0x00);
                length += 2;
                sLength--;
            }
        }
        return length;
    }



    /**
     * <p>Overwrites the super class' method to cope with a redundancy:
     * the property count is maintained in a separate member variable, but
     * shouldn't.</p>
     * 
     * @return The number of properties in this section
     */
    public int getPropertyCount()
    {
        return preprops.size();
    }



    /**
     * <p>Returns this section's properties.</p>
     * 
     * @return this section's properties.
     */
    public Property[] getProperties()
    {
        properties = (Property[]) preprops.toArray(new Property[0]);
        return properties;
    }



    /**
     * <p>Gets a property.</p>
     * 
     * <p><strong>FIXME (2):</strong> This method ensures that properties and
     * preprops are in sync. Cleanup this awful stuff!</p>
     * 
     * @param id The ID of the property to get
     * @return The property or <code>null</code> if there is no such property
     */
    public Object getProperty(final long id)
    {
        getProperties();
        return super.getProperty(id);
    }



    /**
     * <p>Sets the section's dictionary. All keys in the dictionary must be
     * {@link java.lang.Long} instances, all values must be
     * {@link java.lang.String}s. This method overwrites the properties with IDs
     * 0 and 1 since they are reserved for the dictionary and the dictionary's
     * codepage. Setting these properties explicitly might have surprising
     * effects. An application should never do this but always use this
     * method.</p>
     *
     * @param dictionary The dictionary
     * 
     * @exception IllegalPropertySetDataException if the dictionary's key and
     * value types are not correct.
     * 
     * @see Section#getDictionary()
     */
    public void setDictionary(final Map dictionary)
        throws IllegalPropertySetDataException
    {
        if (dictionary != null)
        {
            for (final Iterator i = dictionary.keySet().iterator();
                 i.hasNext();)
                if (!(i.next() instanceof Long))
                    throw new IllegalPropertySetDataException
                        ("Dictionary keys must be of type Long.");
            for (final Iterator i = dictionary.values().iterator();
                 i.hasNext();)
                if (!(i.next() instanceof String))
                    throw new IllegalPropertySetDataException
                        ("Dictionary values must be of type String.");
            this.dictionary = dictionary;

            /* Set the dictionary property (ID 0). Please note that the second
             * parameter in the method call below is unused because dictionaries
             * don't have a type. */
            setProperty(PropertyIDMap.PID_DICTIONARY, -1, dictionary);

            /* Set the codepage property (ID 1) for the strings used in the 
             * dictionary. HPSF always writes Unicode strings to the
             * dictionary. */
            setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
                        new Integer(Property.CP_UNICODE));
        }
        else
            /* Setting the dictionary to null means to remove property 0.
             * However, it does not mean to remove property 1 (codepage). */
            removeProperty(PropertyIDMap.PID_DICTIONARY);
    }

}