1

I recheck some documentation on tree structures and some relating answers on the subject here on stackoverflow.com, for example this one and they mostly use raw types or just one specific type which does not work for my problem. I have a file which stores meta as an XML, so parsing wasn't a problem but I still need a clean data structure to store the actual information.

The actual part of the file

<File>
  <Meta>
    <Generator>default</Generator>
    <HeaderHash>110EbosCOut9ooqtIza4yHdT9xx+wUAp1VMfPqpQKwM=</HeaderHash>
    <Color/>
    <MemoryProtection>
      <ProtectTitle>False</ProtectTitle>
      <ProtectUserName>False</ProtectUserName>
      <ProtectPassword>True</ProtectPassword>
      <ProtectURL>False</ProtectURL>
      <ProtectNotes>False</ProtectNotes>
    </MemoryProtection>
    <CustomIcons/>
    <Binaries />
    <CustomData/>
  </Meta>
</<File>

The data structure I came up with

public class MetadataEntry {

    public MetadataEntry() {
        this.entity = new Entry();
    }

    private class Entry<T> {
        private String name;
        private T value;
        private boolean isGroup;

        private void setValue(T value) {

            if (value instanceof String) {
                this.value = value;
                this.isGroup = false;
            }

            if (value instanceof MetadataEntry) {
                if (isGroup()) {
                    ((List<MetadataEntry>) this.value).add((MetadataEntry) value);
                } else {
                    this.value = (T) new ArrayList<MetadataEntry>();
                    this.isGroup = true;
                    setValue(value);
                }
            }
        }

    }

    private Entry entity;


    public void setName(String name) {
        this.entity.name = name;
    }

    public String getName() {
        return this.entity.name;
    }

    public void setValue(String value) {
        entity.setValue(value);
    }

    public void setValue(MetadataEntry value) {
        entity.setValue(value);
    }

    public boolean isGroup() {
        return this.entity.isGroup;
    }

    public List<MetadataEntity> getChildNodes() {
        if (isGroup()) {
            return (List<MetadataEntry>) this.entity.value;
        }

        return null;
    }

    public String getValue() {
        if (!isGroup()) {
            return (String) this.entity.value;
        }

        return null;
    }
}

So, could you suggest how to make it cleaner, without using raw types if it is clearly not necessary and avoid using instanceof? Or even better point me out please where to read more about it.

Argumentative comments would be much more appreciated instead of down-voting if for some reason the topic is not of interest.

Community
  • 1
  • 1
Katherine
  • 576
  • 1
  • 7
  • 19
  • Yes, you should avoid using `instaceof` until you can prove it's the best option. In this case it's not but it looks to be a side effect of a design issue. Can you clarify the tree you mentioned and explain why you have a list as a potential value? – ChiefTwoPencils Dec 28 '16 at 02:22

1 Answers1

1

Composite Pattern will be suitable for your case, citing 《Design Patterns》GoF

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

The following code composites your tree structure with Composite Pattern applied.

/**
 * declares the interface for objects in the composition
 * @param <T>
 */
interface MetaEntry<T> extends Iterable<MetaEntry<T>> {
    T getName();
    MetaEntry<T> setName(T name);
    T getValue();
    MetaEntry<T> setValue(T name);
    MetaEntry<T> add(MetaEntry<T> entry);
    MetaEntry<T> remove(MetaEntry<T> entry);
}
/**
 * represents leaf objects in the composition. A leaf has no children.
 * @param <T>
 */
class SimpleMetaEntry<T> implements MetaEntry<T> {
    T name;
    T value;
    @Override
    public T getName() {
        return name;
    }

    @Override
    public MetaEntry<T> setName(T name) {
        this.name = name;
        return this;
    }

    @Override
    public T getValue() {
        return value;
    }

    @Override
    public MetaEntry<T> setValue(T value) {
        this.value = value;
        return this;
    }

    @Override
    public MetaEntry<T> add(MetaEntry<T> entry) {
        throw new UnsupportedOperationException();
    }

    @Override
    public MetaEntry<T> remove(MetaEntry<T> entry) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Iterator<MetaEntry<T>> iterator() {

        // we want to treat all objects uniformly.
        return  new Iterator<MetaEntry<T>>() {
            @Override
            public boolean hasNext() {
                return false;
            }

            @Override
            public MetaEntry<T> next() {
                return null;
            }
        };
    }

    @Override
    public String toString() {
        return "<" + name + (value == null ? "/>" : ">" + value + "</" + name + ">\n");
    }
}

/**
 * defines behavior for components having children.
 * @param <T>
 */
class CompositeMetaEntry<T> implements MetaEntry<T> {
    T name;
    T value;
    List<MetaEntry<T>> list = new ArrayList<MetaEntry<T>>();
    @Override
    public T getName() {
        return name;
    }

    @Override
    public MetaEntry<T> setName(T name) {
        this.name = name;
        return this;
    }

    @Override
    public T getValue() {
        return value;
    }

    @Override
    public MetaEntry<T> setValue(T value) {
        this.value = value;
        return this;
    }

    @Override
    public MetaEntry<T> add(MetaEntry<T> entry) {
       list.add(entry);
        return this;
    }

    @Override
    public MetaEntry<T> remove(MetaEntry<T> entry) {
        list.remove(entry);
        return this;
    }

    @Override
    public Iterator<MetaEntry<T>> iterator() {
        return  list.iterator();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();

        builder.append("<" + name + ">");

        for(MetaEntry<T> metaEntry : this) {
            builder.append(metaEntry.toString());
        }
        builder.append("</" + name + ">");

        return builder.toString();
    }
}

public static void main(String[] args) throws Exception {
        MetaEntry<String> compositeMetaEntry = new CompositeMetaEntry<String>();
        compositeMetaEntry.setName("File");
        compositeMetaEntry.add(
                new CompositeMetaEntry<String>().setName("Meta").add(
                        new SimpleMetaEntry<String>().setName("Generator").setValue("default")
                ).add(
                        new SimpleMetaEntry<String>().setName("HeaderHash").setValue("110EbosCOut9ooqtIza4yHdT9xx+wUAp1VMfPqpQKwM=")
                ).add(
                        new SimpleMetaEntry<String>().setName("Color")
                ).add(
                        new CompositeMetaEntry<String>().setName("MemoryProtection").add(
                                new SimpleMetaEntry<String>().setName("ProtectTitle").setValue("False")
                        ).add(
                                new SimpleMetaEntry<String>().setName("ProtectUserName").setValue("False")
                        ).add(
                                new SimpleMetaEntry<String>().setName("ProtectPassword").setValue("True")
                        ).add(
                                new SimpleMetaEntry<String>().setName("ProtectURL").setValue("False")
                        ).add(
                                new SimpleMetaEntry<String>().setName("ProtectNotes").setValue("false")
                        )
                ).add(
                        new SimpleMetaEntry<String>().setName("CustomIcons")
                ).add(
                        new SimpleMetaEntry<String>().setName("Binaries")
                ).add(
                        new SimpleMetaEntry<String>().setName("CustomData")
                )
        );

        System.out.println(compositeMetaEntry);

}
Jerry Chin
  • 657
  • 1
  • 8
  • 25