package Examples;

// Copyright (c) 2001-2025 Aspose Pty Ltd. All Rights Reserved.
//
// This file is part of Aspose.Words. The source code in this file
// is only intended as a supplement to the documentation, and is provided
// "as is", without warranty of any kind, either expressed or implied.
//////////////////////////////////////////////////////////////////////////

import com.aspose.words.*;
import org.apache.commons.io.FileUtils;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.File;
import java.io.FileInputStream;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.Date;
import java.util.Iterator;

@Test
public class ExDocumentProperties extends ApiExampleBase {
    @Test
    public void custom() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.Item(String)
        //ExFor:CustomDocumentProperties
        //ExFor:DocumentProperty.ToString
        //ExFor:DocumentPropertyCollection.Count
        //ExFor:DocumentPropertyCollection.Item(int)
        //ExSummary:Shows how to work with custom document properties.
        Document doc = new Document(getMyDir() + "Properties.docx");

        // Every document contains a collection of custom properties, which, like the built-in properties, are key-value pairs.
        // The document has a fixed list of built-in properties. The user creates all of the custom properties. 
        Assert.assertEquals("Value of custom document property", doc.getCustomDocumentProperties().get("CustomProperty").toString());

        doc.getCustomDocumentProperties().add("CustomProperty2", "Value of custom document property #2");

        System.out.println("Custom Properties:");
        for (DocumentProperty customDocumentProperty : doc.getCustomDocumentProperties()) {
            System.out.println(customDocumentProperty.getName());
            System.out.println(MessageFormat.format("\tType:\t{0}", customDocumentProperty.getType()));
            System.out.println(MessageFormat.format("\tValue:\t\"{0}\"", customDocumentProperty.getValue()));
        }
        //ExEnd

        Assert.assertEquals(2, doc.getCustomDocumentProperties().getCount());
    }

    @Test
    public void description() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.Author
        //ExFor:BuiltInDocumentProperties.Category
        //ExFor:BuiltInDocumentProperties.Comments
        //ExFor:BuiltInDocumentProperties.Keywords
        //ExFor:BuiltInDocumentProperties.Subject
        //ExFor:BuiltInDocumentProperties.Title
        //ExSummary:Shows how to work with built-in document properties in the "Description" category.
        Document doc = new Document();
        DocumentBuilder builder = new DocumentBuilder(doc);
        BuiltInDocumentProperties properties = doc.getBuiltInDocumentProperties();

        // Below are four built-in document properties that have fields that can display their values in the document body.
        // 1 -  "Author" property, which we can display using an AUTHOR field:
        properties.setAuthor("John Doe");
        builder.write("Author:\t");
        builder.insertField(FieldType.FIELD_AUTHOR, true);

        // 2 -  "Title" property, which we can display using a TITLE field:
        properties.setTitle("John's Document");
        builder.write("\nDoc title:\t");
        builder.insertField(FieldType.FIELD_TITLE, true);

        // 3 -  "Subject" property, which we can display using a SUBJECT field:
        properties.setSubject("My subject");
        builder.write("\nSubject:\t");
        builder.insertField(FieldType.FIELD_SUBJECT, true);

        // 4 -  "Comments" property, which we can display using a COMMENTS field:
        properties.setComments(MessageFormat.format("This is {0}''s document about {1}", properties.getAuthor(), properties.getSubject()));
        builder.write("\nComments:\t\"");
        builder.insertField(FieldType.FIELD_COMMENTS, true);
        builder.write("\"");

        // The "Category" built-in property does not have a field that can display its value.
        properties.setCategory("My category");

        // We can set multiple keywords for a document by separating the string value of the "Keywords" property with semicolons.
        properties.setKeywords("Tag 1; Tag 2; Tag 3");

        // We can right-click this document in Windows Explorer and find these properties in "Properties" -> "Details".
        // The "Author" built-in property is in the "Origin" group, and the others are in the "Description" group.
        doc.save(getArtifactsDir() + "DocumentProperties.Description.docx");
        //ExEnd

        doc = new Document(getArtifactsDir() + "DocumentProperties.Description.docx");

        properties = doc.getBuiltInDocumentProperties();

        Assert.assertEquals("John Doe", properties.getAuthor());
        Assert.assertEquals("My category", properties.getCategory());
        Assert.assertEquals("This is John Doe's document about My subject", properties.getComments());
        Assert.assertEquals("Tag 1; Tag 2; Tag 3", properties.getKeywords());
        Assert.assertEquals("My subject", properties.getSubject());
        Assert.assertEquals("John's Document", properties.getTitle());
        Assert.assertEquals("Author:\t\u0013 AUTHOR \u0014John Doe\u0015\r" +
                "Doc title:\t\u0013 TITLE \u0014John's Document\u0015\r" +
                "Subject:\t\u0013 SUBJECT \u0014My subject\u0015\r" +
                "Comments:\t\"\u0013 COMMENTS \u0014This is John Doe's document about My subject\u0015\"", doc.getText().trim());
    }

    @Test
    public void origin() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.Company
        //ExFor:BuiltInDocumentProperties.CreatedTime
        //ExFor:BuiltInDocumentProperties.LastPrinted
        //ExFor:BuiltInDocumentProperties.LastSavedBy
        //ExFor:BuiltInDocumentProperties.LastSavedTime
        //ExFor:BuiltInDocumentProperties.Manager
        //ExFor:BuiltInDocumentProperties.NameOfApplication
        //ExFor:BuiltInDocumentProperties.RevisionNumber
        //ExFor:BuiltInDocumentProperties.Template
        //ExFor:BuiltInDocumentProperties.TotalEditingTime
        //ExFor:BuiltInDocumentProperties.Version
        //ExSummary:Shows how to work with document properties in the "Origin" category.
        // Open a document that we have created and edited using Microsoft Word.
        Document doc = new Document(getMyDir() + "Properties.docx");
        BuiltInDocumentProperties properties = doc.getBuiltInDocumentProperties();

        // The following built-in properties contain information regarding the creation and editing of this document.
        // We can right-click this document in Windows Explorer and find
        // these properties via "Properties" -> "Details" -> "Origin" category.
        // Fields such as PRINTDATE and EDITTIME can display these values in the document body.
        System.out.println(MessageFormat.format("Created using {0}, on {1}", properties.getNameOfApplication(), properties.getCreatedTime()));
        System.out.println(MessageFormat.format("Minutes spent editing: {0}", properties.getTotalEditingTime()));
        System.out.println(MessageFormat.format("Date/time last printed: {0}", properties.getLastPrinted()));
        System.out.println(MessageFormat.format("Template document: {0}", properties.getTemplate()));

        // We can also change the values of built-in properties.
        properties.setCompany("Doe Ltd.");
        properties.setManager("Jane Doe");
        properties.setVersion(5);
        properties.setRevisionNumber(properties.getRevisionNumber() + 1);

        // Microsoft Word updates the following properties automatically when we save the document.
        // To use these properties with Aspose.Words, we will need to set values for them manually.
        properties.setLastSavedBy("John Doe");
        properties.setLastSavedTime(new Date());

        // We can right-click this document in Windows Explorer and find these properties in "Properties" -> "Details" -> "Origin".
        doc.save(getArtifactsDir() + "DocumentProperties.Origin.docx");
        //ExEnd

        properties = new Document(getArtifactsDir() + "DocumentProperties.Origin.docx").getBuiltInDocumentProperties();

        Assert.assertEquals("Doe Ltd.", properties.getCompany());
        Assert.assertEquals("John Doe", properties.getLastSavedBy());
        TestUtil.verifyDate(new Date(), properties.getLastSavedTime(), Duration.ofSeconds(5));
        Assert.assertEquals("Jane Doe", properties.getManager());
        Assert.assertEquals("Microsoft Office Word", properties.getNameOfApplication());
        Assert.assertEquals(12, properties.getRevisionNumber());
        Assert.assertEquals("Normal", properties.getTemplate());
        Assert.assertEquals(8, properties.getTotalEditingTime());
        Assert.assertEquals(786432, properties.getVersion());
    }

    //ExStart
    //ExFor:BuiltInDocumentProperties.Bytes
    //ExFor:BuiltInDocumentProperties.Characters
    //ExFor:BuiltInDocumentProperties.CharactersWithSpaces
    //ExFor:BuiltInDocumentProperties.ContentStatus
    //ExFor:BuiltInDocumentProperties.ContentType
    //ExFor:BuiltInDocumentProperties.Lines
    //ExFor:BuiltInDocumentProperties.LinksUpToDate
    //ExFor:BuiltInDocumentProperties.Pages
    //ExFor:BuiltInDocumentProperties.Paragraphs
    //ExFor:BuiltInDocumentProperties.Words
    //ExSummary:Shows how to work with document properties in the "Content" category.
    @Test //ExSkip
    public void content() throws Exception {
        Document doc = new Document(getMyDir() + "Paragraphs.docx");
        BuiltInDocumentProperties properties = doc.getBuiltInDocumentProperties();

        // By using built in properties,
        // we can treat document statistics such as word/page/character counts as metadata that can be glanced at without opening the document
        // These properties are accessed by right clicking the file in Windows Explorer and navigating to Properties > Details > Content
        // If we want to display this data inside the document, we can use fields such as NUMPAGES, NUMWORDS, NUMCHARS etc.
        // Also, these values can also be viewed in Microsoft Word by navigating File > Properties > Advanced Properties > Statistics
        // Page count: The PageCount property shows the page count in real time and its value can be assigned to the Pages property

        // The "Pages" property stores the page count of the document. 
        Assert.assertEquals(6, properties.getPages());

        // The "Words", "Characters", and "CharactersWithSpaces" built-in properties also display various document statistics,
        // but we need to call the "UpdateWordCount" method on the whole document before we can expect them to contain accurate values.
        Assert.assertEquals(1054, properties.getWords()); //ExSkip
        Assert.assertEquals(6009, properties.getCharacters()); //ExSkip
        Assert.assertEquals(7049, properties.getCharactersWithSpaces()); //ExSkip
        doc.updateWordCount();

        Assert.assertEquals(1035, properties.getWords());
        Assert.assertEquals(6026, properties.getCharacters());
        Assert.assertEquals(7041, properties.getCharactersWithSpaces());

        // Count the number of lines in the document, and then assign the result to the "Lines" built-in property.
        LineCounter lineCounter = new LineCounter(doc);
        properties.setLines(lineCounter.getLineCount());

        Assert.assertEquals(142, properties.getLines());

        // Assign the number of Paragraph nodes in the document to the "Paragraphs" built-in property.
        properties.setParagraphs(doc.getChildNodes(NodeType.PARAGRAPH, true).getCount());
        Assert.assertEquals(29, properties.getParagraphs());

        // Get an estimate of the file size of our document via the "Bytes" built-in property.
        Assert.assertEquals(20310, properties.getBytes());

        // Set a different template for our document, and then update the "Template" built-in property manually to reflect this change.
        doc.setAttachedTemplate(getMyDir() + "Business brochure.dotx");

        Assert.assertEquals("Normal", properties.getTemplate());

        properties.setTemplate(doc.getAttachedTemplate());

        // "ContentStatus" is a descriptive built-in property.
        properties.setContentStatus("Draft");

        // Upon saving, the "ContentType" built-in property will contain the MIME type of the output save format.
        Assert.assertEquals("", properties.getContentType());

        // If the document contains links, and they are all up to date, we can set the "LinksUpToDate" property to "true".
        Assert.assertFalse(properties.getLinksUpToDate());

        doc.save(getArtifactsDir() + "DocumentProperties.Content.docx");
        testContent(new Document(getArtifactsDir() + "DocumentProperties.Content.docx")); //ExSkip
    }

    /// <summary>
    /// Counts the lines in a document.
    /// Traverses the document's layout entities tree upon construction,
    /// counting entities of the "Line" type that also contain real text.
    /// </summary>
    private static class LineCounter {
        public LineCounter(Document doc) throws Exception {
            mLayoutEnumerator = new LayoutEnumerator(doc);

            countLines();
        }

        public int getLineCount() {
            return mLineCount;
        }

        private void countLines() throws Exception {
            do {
                if (mLayoutEnumerator.getType() == LayoutEntityType.LINE) {
                    mScanningLineForRealText = true;
                }

                if (mLayoutEnumerator.moveFirstChild()) {
                    if (mScanningLineForRealText && mLayoutEnumerator.getKind().startsWith("TEXT")) {
                        mLineCount++;
                        mScanningLineForRealText = false;
                    }
                    countLines();
                    mLayoutEnumerator.moveParent();
                }
            } while (mLayoutEnumerator.moveNext());
        }

        private final LayoutEnumerator mLayoutEnumerator;
        private int mLineCount;
        private boolean mScanningLineForRealText;
    }
    //ExEnd

    private void testContent(Document doc) {
        BuiltInDocumentProperties properties = doc.getBuiltInDocumentProperties();

        Assert.assertEquals(6, properties.getPages());

        Assert.assertEquals(1035, properties.getWords());
        Assert.assertEquals(6026, properties.getCharacters());
        Assert.assertEquals(7041, properties.getCharactersWithSpaces());
        Assert.assertEquals(142, properties.getLines());
        Assert.assertEquals(29, properties.getParagraphs());
        Assert.assertEquals(15800.0, properties.getBytes(), 200.0);
        Assert.assertEquals(getMyDir().replace("\\\\", "\\") + "Business brochure.dotx", properties.getTemplate());
        Assert.assertEquals("Draft", properties.getContentStatus());
        Assert.assertEquals("", properties.getContentType());
        Assert.assertFalse(properties.getLinksUpToDate());
    }

    @Test
    public void thumbnail() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.Thumbnail
        //ExFor:DocumentProperty.ToByteArray
        //ExSummary:Shows how to add a thumbnail to a document that we save as an Epub.
        Document doc = new Document();
        DocumentBuilder builder = new DocumentBuilder(doc);
        builder.writeln("Hello world!");

        // If we save a document, whose "Thumbnail" property contains image data that we added, as an Epub,
        // a reader that opens that document may display the image before the first page.
        BuiltInDocumentProperties properties = doc.getBuiltInDocumentProperties();

        byte[] thumbnailBytes = DocumentHelper.getBytesFromStream(new FileInputStream(getImageDir() + "Logo.jpg"));
        properties.setThumbnail(thumbnailBytes);

        doc.save(getArtifactsDir() + "DocumentProperties.Thumbnail.epub");

        // We can extract a document's thumbnail image and save it to the local file system.
        DocumentProperty thumbnail = doc.getBuiltInDocumentProperties().get("Thumbnail");
        FileUtils.writeByteArrayToFile(new File(getArtifactsDir() + "DocumentProperties.Thumbnail.gif"), thumbnail.toByteArray());
        //ExEnd
    }

    @Test
    public void hyperlinkBase() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.HyperlinkBase
        //ExSummary:Shows how to store the base part of a hyperlink in the document's properties.
        Document doc = new Document();
        DocumentBuilder builder = new DocumentBuilder(doc);

        // Insert a relative hyperlink to a document in the local file system named "Document.docx".
        // Clicking on the link in Microsoft Word will open the designated document, if it is available.
        builder.insertHyperlink("Relative hyperlink", "Document.docx", false);

        // This link is relative. If there is no "Document.docx" in the same folder
        // as the document that contains this link, the link will be broken.
        Assert.assertFalse(new File(getArtifactsDir() + "Document.docx").exists());
        doc.save(getArtifactsDir() + "DocumentProperties.HyperlinkBase.BrokenLink.docx");

        // The document we are trying to link to is in a different directory to the one we are planning to save the document in.
        // We could fix links like this by putting an absolute filename in each one. 
        // Alternatively, we could provide a base link that every hyperlink with a relative filename
        // will prepend to its link when we click on it. 
        BuiltInDocumentProperties properties = doc.getBuiltInDocumentProperties();
        properties.setHyperlinkBase(getMyDir());

        Assert.assertTrue(new File(properties.getHyperlinkBase() + ((FieldHyperlink) doc.getRange().getFields().get(0)).getAddress()).exists());

        doc.save(getArtifactsDir() + "DocumentProperties.HyperlinkBase.WorkingLink.docx");
        //ExEnd

        doc = new Document(getArtifactsDir() + "DocumentProperties.HyperlinkBase.BrokenLink.docx");
        properties = doc.getBuiltInDocumentProperties();

        Assert.assertEquals("", properties.getHyperlinkBase());

        doc = new Document(getArtifactsDir() + "DocumentProperties.HyperlinkBase.WorkingLink.docx");
        properties = doc.getBuiltInDocumentProperties();

        Assert.assertEquals(getMyDir(), properties.getHyperlinkBase());
        Assert.assertTrue(new File(properties.getHyperlinkBase() + ((FieldHyperlink) doc.getRange().getFields().get(0)).getAddress()).exists());
    }

    @Test
    public void headingPairs() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.HeadingPairs
        //ExFor:BuiltInDocumentProperties.TitlesOfParts
        //ExSummary:Shows the relationship between "HeadingPairs" and "TitlesOfParts" properties.
        Document doc = new Document(getMyDir() + "Heading pairs and titles of parts.docx");

        // We can find the combined values of these collections via
        // "File" -> "Properties" -> "Advanced Properties" -> "Contents" tab.
        // The HeadingPairs property is a collection of <string, int> pairs that
        // determines how many document parts a heading spans across.
        Object[] headingPairs = doc.getBuiltInDocumentProperties().getHeadingPairs();

        // The TitlesOfParts property contains the names of parts that belong to the above headings.
        String[] titlesOfParts = doc.getBuiltInDocumentProperties().getTitlesOfParts();

        int headingPairsIndex = 0;
        int titlesOfPartsIndex = 0;
        while (headingPairsIndex < headingPairs.length) {
            System.out.println(MessageFormat.format("Parts for {0}:", headingPairs[headingPairsIndex++]));
            int partsCount = (int) headingPairs[headingPairsIndex++];

            for (int i = 0; i < partsCount; i++)
                System.out.println(MessageFormat.format("\t\"{0}\"", titlesOfParts[titlesOfPartsIndex++]));
        }
        //ExEnd

        // There are 6 array elements designating 3 heading/part count pairs
        Assert.assertEquals(6, headingPairs.length);
        Assert.assertEquals("Title", headingPairs[0].toString());
        Assert.assertEquals("1", headingPairs[1].toString());
        Assert.assertEquals("Heading 1", headingPairs[2].toString());
        Assert.assertEquals("5", headingPairs[3].toString());
        Assert.assertEquals("Heading 2", headingPairs[4].toString());
        Assert.assertEquals("2", headingPairs[5].toString());

        Assert.assertEquals(8, titlesOfParts.length);
        // "Title"
        Assert.assertEquals("", titlesOfParts[0]);
        // "Heading 1"
        Assert.assertEquals("Part1", titlesOfParts[1]);
        Assert.assertEquals("Part2", titlesOfParts[2]);
        Assert.assertEquals("Part3", titlesOfParts[3]);
        Assert.assertEquals("Part4", titlesOfParts[4]);
        Assert.assertEquals("Part5", titlesOfParts[5]);
        // "Heading 2"
        Assert.assertEquals("Part6", titlesOfParts[6]);
        Assert.assertEquals("Part7", titlesOfParts[7]);
    }

    @Test
    public void security() throws Exception {
        //ExStart
        //ExFor:BuiltInDocumentProperties.Security
        //ExFor:DocumentSecurity
        //ExSummary:Shows how to use document properties to display the security level of a document.
        Document doc = new Document();

        Assert.assertEquals(DocumentSecurity.NONE, doc.getBuiltInDocumentProperties().getSecurity());

        // If we configure a document to be read-only, it will display this status using the "Security" built-in property.
        doc.getWriteProtection().setReadOnlyRecommended(true);
        doc.save(getArtifactsDir() + "DocumentProperties.Security.ReadOnlyRecommended.docx");

        Assert.assertEquals(DocumentSecurity.READ_ONLY_RECOMMENDED,
                new Document(getArtifactsDir() + "DocumentProperties.Security.ReadOnlyRecommended.docx").getBuiltInDocumentProperties().getSecurity());

        // Write-protect a document, and then verify its security level.
        doc = new Document();

        Assert.assertFalse(doc.getWriteProtection().isWriteProtected());

        doc.getWriteProtection().setPassword("MyPassword");

        Assert.assertTrue(doc.getWriteProtection().validatePassword("MyPassword"));
        Assert.assertTrue(doc.getWriteProtection().isWriteProtected());

        doc.save(getArtifactsDir() + "DocumentProperties.Security.ReadOnlyEnforced.docx");

        Assert.assertEquals(DocumentSecurity.READ_ONLY_ENFORCED,
                new Document(getArtifactsDir() + "DocumentProperties.Security.ReadOnlyEnforced.docx").getBuiltInDocumentProperties().getSecurity());

        // "Security" is a descriptive property. We can edit its value manually.
        doc = new Document();

        doc.protect(ProtectionType.ALLOW_ONLY_COMMENTS, "MyPassword");
        doc.getBuiltInDocumentProperties().setSecurity(DocumentSecurity.READ_ONLY_EXCEPT_ANNOTATIONS);
        doc.save(getArtifactsDir() + "DocumentProperties.Security.ReadOnlyExceptAnnotations.docx");

        Assert.assertEquals(DocumentSecurity.READ_ONLY_EXCEPT_ANNOTATIONS,
                new Document(getArtifactsDir() + "DocumentProperties.Security.ReadOnlyExceptAnnotations.docx").getBuiltInDocumentProperties().getSecurity());
        //ExEnd
    }

    @Test
    public void customNamedAccess() throws Exception {
        //ExStart
        //ExFor:DocumentPropertyCollection.Item(String)
        //ExFor:CustomDocumentProperties.Add(String,DateTime)
        //ExFor:DocumentProperty.ToDateTime
        //ExSummary:Shows how to create a custom document property which contains a date and time.
        Document doc = new Document();

        doc.getCustomDocumentProperties().add("AuthorizationDate", new Date());

        System.out.println(MessageFormat.format("Document authorized on {0}", doc.getCustomDocumentProperties().get("AuthorizationDate")));
        //ExEnd

        TestUtil.verifyDate(new Date(),
                DocumentHelper.saveOpen(doc).getCustomDocumentProperties().get("AuthorizationDate").toDateTime(),
                Duration.ofSeconds(1));
    }

    @Test
    public void linkCustomDocumentPropertiesToBookmark() throws Exception {
        //ExStart
        //ExFor:CustomDocumentProperties.AddLinkToContent(String, String)
        //ExFor:DocumentProperty.IsLinkToContent
        //ExFor:DocumentProperty.LinkSource
        //ExSummary:Shows how to link a custom document property to a bookmark.
        Document doc = new Document();
        DocumentBuilder builder = new DocumentBuilder(doc);

        builder.startBookmark("MyBookmark");
        builder.write("Hello world!");
        builder.endBookmark("MyBookmark");

        // Link a new custom property to a bookmark. The value of this property
        // will be the contents of the bookmark that it references in the "LinkSource" member.
        CustomDocumentProperties customProperties = doc.getCustomDocumentProperties();
        DocumentProperty customProperty = customProperties.addLinkToContent("Bookmark", "MyBookmark");

        Assert.assertEquals(true, customProperty.isLinkToContent());
        Assert.assertEquals("MyBookmark", customProperty.getLinkSource());
        Assert.assertEquals("Hello world!", customProperty.getValue());

        doc.save(getArtifactsDir() + "DocumentProperties.LinkCustomDocumentPropertiesToBookmark.docx");
        //ExEnd

        doc = new Document(getArtifactsDir() + "DocumentProperties.LinkCustomDocumentPropertiesToBookmark.docx");
        customProperty = doc.getCustomDocumentProperties().get("Bookmark");

        Assert.assertEquals(true, customProperty.isLinkToContent());
        Assert.assertEquals("MyBookmark", customProperty.getLinkSource());
        Assert.assertEquals("Hello world!", customProperty.getValue());
    }

    @Test
    public void documentPropertyCollection() throws Exception {
        //ExStart
        //ExFor:CustomDocumentProperties.Add(String,String)
        //ExFor:CustomDocumentProperties.Add(String,Boolean)
        //ExFor:CustomDocumentProperties.Add(String,int)
        //ExFor:CustomDocumentProperties.Add(String,DateTime)
        //ExFor:CustomDocumentProperties.Add(String,Double)
        //ExFor:DocumentProperty.Type
        //ExFor:DocumentPropertyCollection
        //ExFor:DocumentPropertyCollection.Clear
        //ExFor:DocumentPropertyCollection.Contains(String)
        //ExFor:DocumentPropertyCollection.GetEnumerator
        //ExFor:DocumentPropertyCollection.IndexOf(String)
        //ExFor:DocumentPropertyCollection.RemoveAt(Int32)
        //ExFor:DocumentPropertyCollection.Remove
        //ExFor:PropertyType
        //ExSummary:Shows how to work with a document's custom properties.
        Document doc = new Document();
        CustomDocumentProperties properties = doc.getCustomDocumentProperties();

        Assert.assertEquals(0, properties.getCount());

        // Custom document properties are key-value pairs that we can add to the document.
        properties.add("Authorized", true);
        properties.add("Authorized By", "John Doe");
        properties.add("Authorized Date", new Date());
        properties.add("Authorized Revision", doc.getBuiltInDocumentProperties().getRevisionNumber());
        properties.add("Authorized Amount", 123.45);

        // The collection sorts the custom properties in alphabetic order.
        Assert.assertEquals(1, properties.indexOf("Authorized Amount"));
        Assert.assertEquals(5, properties.getCount());

        // Print every custom property in the document.
        Iterator<DocumentProperty> enumerator = properties.iterator();
        while (enumerator.hasNext()) {
            DocumentProperty property = enumerator.next();
            System.out.println(MessageFormat.format("Name: \"{0}\"\n\tType: \"{1}\"\n\tValue: \"{2}\"", property.getName(), property.getType(), property.getValue()));
        }

        // Display the value of a custom property using a DOCPROPERTY field.
        DocumentBuilder builder = new DocumentBuilder(doc);
        FieldDocProperty field = (FieldDocProperty) builder.insertField(" DOCPROPERTY \"Authorized By\"");
        field.update();

        Assert.assertEquals("John Doe", field.getResult());

        // We can find these custom properties in Microsoft Word via "File" -> "Properties" > "Advanced Properties" > "Custom".
        doc.save(getArtifactsDir() + "DocumentProperties.DocumentPropertyCollection.docx");

        // Below are three ways or removing custom properties from a document.
        // 1 -  Remove by index:
        properties.removeAt(1);

        Assert.assertFalse(properties.contains("Authorized Amount"));
        Assert.assertEquals(4, properties.getCount());

        // 2 -  Remove by name:
        properties.remove("Authorized Revision");

        Assert.assertFalse(properties.contains("Authorized Revision"));
        Assert.assertEquals(3, properties.getCount());

        // 3 -  Empty the entire collection at once:
        properties.clear();

        Assert.assertEquals(0, properties.getCount());
        //ExEnd
    }

    @Test
    public void propertyTypes() throws Exception {
        //ExStart
        //ExFor:DocumentProperty.ToBool
        //ExFor:DocumentProperty.ToInt
        //ExFor:DocumentProperty.ToDouble
        //ExFor:DocumentProperty.ToString
        //ExFor:DocumentProperty.ToDateTime
        //ExSummary:Shows various type conversion methods of custom document properties.
        Document doc = new Document();
        CustomDocumentProperties properties = doc.getCustomDocumentProperties();

        Date authDate = new Date();
        properties.add("Authorized", true);
        properties.add("Authorized By", "John Doe");
        properties.add("Authorized Date", authDate);
        properties.add("Authorized Revision", doc.getBuiltInDocumentProperties().getRevisionNumber());
        properties.add("Authorized Amount", 123.45);

        Assert.assertEquals(true, properties.get("Authorized").toBool());
        Assert.assertEquals("John Doe", properties.get("Authorized By").toString());
        Assert.assertEquals(authDate, properties.get("Authorized Date").toDateTime());
        Assert.assertEquals(1, properties.get("Authorized Revision").toInt());
        Assert.assertEquals(123.45d, properties.get("Authorized Amount").toDouble());
        //ExEnd
    }

    @Test
    public void extendedProperties() throws Exception
    {
        //ExStart:ExtendedProperties
        //GistId:72d57eeddb7fb342fd51b26e5fcf9642
        //ExFor:BuiltInDocumentProperties.ScaleCrop
        //ExFor:BuiltInDocumentProperties.SharedDocument
        //ExFor:BuiltInDocumentProperties.HyperlinksChanged
        //ExSummary:Shows how to get extended properties.
        Document doc = new Document(getMyDir() + "Extended properties.docx");
        Assert.assertTrue(doc.getBuiltInDocumentProperties().getScaleCrop());
        Assert.assertTrue(doc.getBuiltInDocumentProperties().getSharedDocument());
        Assert.assertTrue(doc.getBuiltInDocumentProperties().getHyperlinksChanged());
        //ExEnd:ExtendedProperties
    }
}
