| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE examples.
 -    Copyright (c) 2017 - ROLI Ltd.
 - 
 -    The code included in this file is provided under the terms of the ISC license
 -    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -    To use, copy, modify, and/or distribute this software for any purpose with or
 -    without fee is hereby granted provided that the above copyright notice and
 -    this permission notice appear in all copies.
 - 
 -    THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 -    WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 -    PURPOSE, ARE DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - /*******************************************************************************
 -  The block below describes the properties of this PIP. A PIP is a short snippet
 -  of code that can be read by the Projucer and used to generate a JUCE project.
 - 
 -  BEGIN_JUCE_PIP_METADATA
 - 
 -  name:             XMLandJSONDemo
 -  version:          1.0.0
 -  vendor:           JUCE
 -  website:          http://juce.com
 -  description:      Reads XML and JSON files.
 - 
 -  dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 -                    juce_gui_basics, juce_gui_extra
 -  exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 - 
 -  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 - 
 -  type:             Component
 -  mainClass:        XMLandJSONDemo
 - 
 -  useLocalCopy:     1
 - 
 -  END_JUCE_PIP_METADATA
 - 
 - *******************************************************************************/
 - 
 - #pragma once
 - 
 - #include "../Assets/DemoUtilities.h"
 - 
 - //==============================================================================
 - class XmlTreeItem  : public TreeViewItem
 - {
 - public:
 -     XmlTreeItem (XmlElement& x)  : xml (x)    {}
 - 
 -     String getUniqueName() const override
 -     {
 -         if (xml.getTagName().isEmpty())
 -             return "unknown";
 - 
 -         return xml.getTagName();
 -     }
 - 
 -     bool mightContainSubItems() override
 -     {
 -         return xml.getFirstChildElement() != nullptr;
 -     }
 - 
 -     void paintItem (Graphics& g, int width, int height) override
 -     {
 -         // if this item is selected, fill it with a background colour..
 -         if (isSelected())
 -             g.fillAll (Colours::blue.withAlpha (0.3f));
 - 
 -         // use a "colour" attribute in the xml tag for this node to set the text colour..
 -         g.setColour (Colour::fromString (xml.getStringAttribute ("colour", "ff000000")));
 -         g.setFont (height * 0.7f);
 - 
 -         // draw the xml element's tag name..
 -         g.drawText (xml.getTagName(),
 -                     4, 0, width - 4, height,
 -                     Justification::centredLeft, true);
 -     }
 - 
 -     void itemOpennessChanged (bool isNowOpen) override
 -     {
 -         if (isNowOpen)
 -         {
 -             // if we've not already done so, we'll now add the tree's sub-items. You could
 -             // also choose to delete the existing ones and refresh them if that's more suitable
 -             // in your app.
 -             if (getNumSubItems() == 0)
 -             {
 -                 // create and add sub-items to this node of the tree, corresponding to
 -                 // each sub-element in the XML..
 - 
 -                 forEachXmlChildElement (xml, child)
 -                 {
 -                     jassert (child != nullptr);
 -                     addSubItem (new XmlTreeItem (*child));
 -                 }
 -             }
 -         }
 -         else
 -         {
 -             // in this case, we'll leave any sub-items in the tree when the node gets closed,
 -             // though you could choose to delete them if that's more appropriate for
 -             // your application.
 -         }
 -     }
 - 
 - private:
 -     XmlElement& xml;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTreeItem)
 - };
 - 
 - //==============================================================================
 - class JsonTreeItem  : public TreeViewItem
 - {
 - public:
 -     JsonTreeItem (Identifier i, var value)
 -         : identifier (i),
 -           json (value)
 -     {}
 - 
 -     String getUniqueName() const override
 -     {
 -         return identifier.toString() + "_id";
 -     }
 - 
 -     bool mightContainSubItems() override
 -     {
 -         if (auto* obj = json.getDynamicObject())
 -             return obj->getProperties().size() > 0;
 - 
 -         return json.isArray();
 -     }
 - 
 -     void paintItem (Graphics& g, int width, int height) override
 -     {
 -         // if this item is selected, fill it with a background colour..
 -         if (isSelected())
 -             g.fillAll (Colours::blue.withAlpha (0.3f));
 - 
 -         g.setColour (Colours::black);
 -         g.setFont (height * 0.7f);
 - 
 -         // draw the element's tag name..
 -         g.drawText (getText(),
 -                     4, 0, width - 4, height,
 -                     Justification::centredLeft, true);
 -     }
 - 
 -     void itemOpennessChanged (bool isNowOpen) override
 -     {
 -         if (isNowOpen)
 -         {
 -             // if we've not already done so, we'll now add the tree's sub-items. You could
 -             // also choose to delete the existing ones and refresh them if that's more suitable
 -             // in your app.
 -             if (getNumSubItems() == 0)
 -             {
 -                 // create and add sub-items to this node of the tree, corresponding to
 -                 // the type of object this var represents
 - 
 -                 if (json.isArray())
 -                 {
 -                     for (int i = 0; i < json.size(); ++i)
 -                     {
 -                         auto& child = json[i];
 -                         jassert (! child.isVoid());
 -                         addSubItem (new JsonTreeItem ({}, child));
 -                     }
 -                 }
 -                 else if (auto* obj = json.getDynamicObject())
 -                 {
 -                     auto& props = obj->getProperties();
 - 
 -                     for (int i = 0; i < props.size(); ++i)
 -                     {
 -                         auto id = props.getName (i);
 - 
 -                         auto child = props[id];
 -                         jassert (! child.isVoid());
 - 
 -                         addSubItem (new JsonTreeItem (id, child));
 -                     }
 -                 }
 -             }
 -         }
 -         else
 -         {
 -             // in this case, we'll leave any sub-items in the tree when the node gets closed,
 -             // though you could choose to delete them if that's more appropriate for
 -             // your application.
 -         }
 -     }
 - 
 - private:
 -     Identifier identifier;
 -     var json;
 - 
 -     /** Returns the text to display in the tree.
 -         This is a little more complex for JSON than XML as nodes can be strings, objects or arrays.
 -      */
 -     String getText() const
 -     {
 -         String text;
 - 
 -         if (identifier.isValid())
 -             text << identifier.toString();
 - 
 -         if (! json.isVoid())
 -         {
 -             if (text.isNotEmpty() && (! json.isArray()))
 -                 text << ": ";
 - 
 -             if (json.isObject() && (! identifier.isValid()))
 -                 text << "[Array]";
 -             else if (! json.isArray())
 -                 text << json.toString();
 -         }
 - 
 -         return text;
 -     }
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JsonTreeItem)
 - };
 - 
 - //==============================================================================
 - class XMLandJSONDemo   : public Component,
 -                          private CodeDocument::Listener
 - {
 - public:
 -     /** The type of database to parse. */
 -     enum Type
 -     {
 -         xml,
 -         json
 -     };
 - 
 -     XMLandJSONDemo()
 -     {
 -         setOpaque (true);
 - 
 -         addAndMakeVisible (typeBox);
 -         typeBox.addItem ("XML",  1);
 -         typeBox.addItem ("JSON", 2);
 - 
 -         typeBox.onChange = [this]
 -         {
 -             if (typeBox.getSelectedId() == 1)
 -                 reset (xml);
 -             else
 -                 reset (json);
 -         };
 - 
 -         comboBoxLabel.attachToComponent (&typeBox, true);
 - 
 -         addAndMakeVisible (codeDocumentComponent);
 -         codeDocument.addListener (this);
 - 
 -         addAndMakeVisible (resultsTree);
 -         resultsTree.setColour (TreeView::backgroundColourId, Colours::white);
 -         resultsTree.setDefaultOpenness (true);
 - 
 -         addAndMakeVisible (errorMessage);
 -         errorMessage.setReadOnly (true);
 -         errorMessage.setMultiLine (true);
 -         errorMessage.setCaretVisible (false);
 -         errorMessage.setColour (TextEditor::outlineColourId, Colours::transparentWhite);
 -         errorMessage.setColour (TextEditor::shadowColourId,  Colours::transparentWhite);
 - 
 -         typeBox.setSelectedId (1);
 - 
 -         setSize (500, 500);
 -     }
 - 
 -     ~XMLandJSONDemo() override
 -     {
 -         resultsTree.setRootItem (nullptr);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 -     }
 - 
 -     void resized() override
 -     {
 -         auto area = getLocalBounds();
 - 
 -         typeBox.setBounds (area.removeFromTop (36).removeFromRight (150).reduced (8));
 -         codeDocumentComponent.setBounds (area.removeFromTop(area.getHeight() / 2).reduced (8));
 -         resultsTree          .setBounds (area.reduced (8));
 -         errorMessage         .setBounds (resultsTree.getBounds());
 -     }
 - 
 - private:
 -     ComboBox typeBox;
 -     Label comboBoxLabel { {}, "Database Type:" };
 -     CodeDocument codeDocument;
 -     CodeEditorComponent codeDocumentComponent  { codeDocument, nullptr };
 -     TreeView resultsTree;
 - 
 -     std::unique_ptr<TreeViewItem> rootItem;
 -     std::unique_ptr<XmlElement> parsedXml;
 -     TextEditor errorMessage;
 - 
 -     void rebuildTree()
 -     {
 -         std::unique_ptr<XmlElement> openness;
 - 
 -         if (rootItem.get() != nullptr)
 -             openness = rootItem->getOpennessState();
 - 
 -         createNewRootNode();
 - 
 -         if (openness.get() != nullptr && rootItem.get() != nullptr)
 -             rootItem->restoreOpennessState (*openness);
 -     }
 - 
 -     void createNewRootNode()
 -     {
 -         // clear the current tree
 -         resultsTree.setRootItem (nullptr);
 -         rootItem.reset();
 - 
 -         // try and parse the editor's contents
 -         switch (typeBox.getSelectedItemIndex())
 -         {
 -             case xml:           rootItem.reset (rebuildXml());        break;
 -             case json:          rootItem.reset (rebuildJson());       break;
 -             default:            rootItem.reset();                     break;
 -         }
 - 
 -         // if we have a valid TreeViewItem hide any old error messages and set our TreeView to use it
 -         if (rootItem.get() != nullptr)
 -             errorMessage.clear();
 - 
 -         errorMessage.setVisible (! errorMessage.isEmpty());
 - 
 -         resultsTree.setRootItem (rootItem.get());
 -     }
 - 
 -     /** Parses the editors contects as XML. */
 -     TreeViewItem* rebuildXml()
 -     {
 -         parsedXml.reset();
 - 
 -         XmlDocument doc (codeDocument.getAllContent());
 -         parsedXml = doc.getDocumentElement();
 - 
 -         if (parsedXml.get() == nullptr)
 -         {
 -             auto error = doc.getLastParseError();
 - 
 -             if (error.isEmpty())
 -                 error = "Unknown error";
 - 
 -             errorMessage.setText ("Error parsing XML: " + error, dontSendNotification);
 - 
 -             return nullptr;
 -         }
 - 
 -         return new XmlTreeItem (*parsedXml);
 -     }
 - 
 -     /** Parses the editors contects as JSON. */
 -     TreeViewItem* rebuildJson()
 -     {
 -         var parsedJson;
 -         auto result = JSON::parse (codeDocument.getAllContent(), parsedJson);
 - 
 -         if (! result.wasOk())
 -         {
 -             errorMessage.setText ("Error parsing JSON: " + result.getErrorMessage());
 -             return nullptr;
 -         }
 - 
 -         return new JsonTreeItem (Identifier(), parsedJson);
 -     }
 - 
 -     /** Clears the editor and loads some default text. */
 -     void reset (Type type)
 -     {
 -         switch (type)
 -         {
 -             case xml:   codeDocument.replaceAllContent (loadEntireAssetIntoString ("treedemo.xml")); break;
 -             case json:  codeDocument.replaceAllContent (loadEntireAssetIntoString ("juce_module_info")); break;
 -             default:    codeDocument.replaceAllContent ({}); break;
 -         }
 -     }
 - 
 -     void codeDocumentTextInserted (const String&, int) override     { rebuildTree(); }
 -     void codeDocumentTextDeleted (int, int) override                { rebuildTree(); }
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XMLandJSONDemo)
 - };
 
 
  |