com.google.api.explorer.client.parameter.schema.FieldsEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.google.api.explorer.client.parameter.schema.FieldsEditor.java

Source

/*
 * Copyright (C) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.api.explorer.client.parameter.schema;

import com.google.api.explorer.client.Resources;
import com.google.api.explorer.client.base.ApiService;
import com.google.api.explorer.client.base.Schema;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.InlineLabel;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Tree-based UI for selecting fields to request, based on the response schema
 * for a method.
 *
 * @author jasonhall@google.com (Jason Hall)
 */
public class FieldsEditor extends HTMLPanel implements HasValue<Boolean> {

    private static final Joiner JOINER = Joiner.on(',').skipNulls();

    private final ApiService service;
    private final String key;
    private final CheckBox root;
    private final Map<String, HasValue<Boolean>> children = Maps.newHashMap();

    public FieldsEditor(ApiService service, String key) {
        super("");

        this.service = service;
        this.key = key;
        root = new CheckBox(key.isEmpty() ? "Select all/none" : key);
        root.setValue(false);
        root.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
            @Override
            public void onValueChange(ValueChangeEvent<Boolean> event) {
                for (HasValue<Boolean> checkBox : children.values()) {
                    checkBox.setValue(event.getValue(), true);
                }
            }
        });
        add(root);
    }

    /**
     * Sets the properties this field will have, if it is an object.
     */
    public void setProperties(Map<String, Schema> properties) {
        List<String> keys = Lists.newArrayList(properties.keySet());
        Collections.sort(keys);

        HTMLPanel inner = new HTMLPanel("");
        inner.getElement().getStyle().setPaddingLeft(20, Unit.PX);

        for (String childKey : keys) {
            final Schema property = properties.get(childKey);
            final Map<String, Schema> childProperties = property.getProperties();
            final Schema items = property.getItems();

            if (childProperties == null && items == null) {
                // This is a simple field
                CheckBox checkBox = new CheckBox(childKey);
                checkBox.setValue(root.getValue());
                checkBox.setTitle(property.getDescription());
                children.put(childKey, checkBox);
                checkBox.getElement().appendChild(Document.get().createBRElement());
                inner.add(checkBox);
            } else {

                final FieldsEditor editor = new FieldsEditor(service, childKey);
                children.put(childKey, editor);
                inner.add(editor);

                if (childProperties != null) {
                    editor.setProperties(childProperties);
                } else if (property.getRef() != null) {
                    editor.setRef(property.getRef());
                } else if (items != null) {
                    if (items.getProperties() != null) {
                        editor.setProperties(items.getProperties());
                    } else if (items.getRef() != null) {
                        editor.setRef(items.getRef());
                    }
                }
            }
        }
        add(inner);
    }

    /**
     * Denotes that this is an object whose definition is in another Schema, and
     * that it should be filled in with the correct fields when expanded.
     */
    void setRef(final String ref) {
        final InlineLabel expando = new InlineLabel("+");
        add(expando);
        expando.addStyleName(Resources.INSTANCE.style().clickable());
        expando.setTitle("Click to show more fields");
        expando.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                Schema sch = service.getSchemas().get(ref);
                setProperties(sch.getProperties());
                remove(expando);
            }
        });
    }

    /** Returns the string for this field, including sub-fields if it has any. */
    public String genFieldsString() {
        // If there are no children, that means it's an expandable item that is not
        // expanded. Return just the key in this case.
        if (children.isEmpty() && root.getValue()) {
            return key;
        }
        // If all fields are checked, rather than return
        // "object(all,fields,in,the,object)" just return "object"
        if (getValue()) {
            return key;
        }

        // Recursively construct the fields string of all selected children.
        String directFields = JOINER.join(Iterables.transform(children.entrySet(),
                new Function<Map.Entry<String, HasValue<Boolean>>, String>() {
                    @Override
                    public String apply(Entry<String, HasValue<Boolean>> input) {
                        if (input.getValue() instanceof FieldsEditor) {
                            return ((FieldsEditor) input.getValue()).genFieldsString();
                        } else {
                            return input.getValue().getValue() ? input.getKey() : null;
                        }
                    }
                }));

        if (directFields.isEmpty()) {
            // No children were selected, so this object is not needed.
            return null;
        } else if (!directFields.contains(",")) {
            // If only one field is checked, rather than return
            // "object(onlyOneField)", return "object/onlyOneField"
            if (key.isEmpty()) {
                return directFields;
            } else {
                return key + '/' + directFields;
            }
        }

        StringBuilder sb = new StringBuilder();
        if (!key.isEmpty()) {
            sb.append(key).append('(');
        }

        sb.append(directFields);

        if (!key.isEmpty()) {
            sb.append(')');
        }
        return sb.toString();
    }

    /**
     * Returns this field's checked value, or if it has children, whether all its
     * children are checked.
     */
    @Override
    public Boolean getValue() {
        if (children.isEmpty()) {
            return root.getValue();
        }

        return Iterables.all(children.entrySet(), new Predicate<Map.Entry<String, HasValue<Boolean>>>() {
            @Override
            public boolean apply(Entry<String, HasValue<Boolean>> input) {
                return input.getValue().getValue();
            }
        });
    }

    /**
     * Sets this field's checked value, and all of its childrens' if it has any.
     */
    @Override
    public void setValue(Boolean value) {
        for (HasValue<Boolean> hasValue : children.values()) {
            hasValue.setValue(value);
        }
        this.root.setValue(value);
    }

    /** Adds a ValueChangeHandler to this field. */
    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Boolean> handler) {
        return root.addValueChangeHandler(handler);
    }

    /** Sets this checkboxes value. */
    @Override
    public void setValue(Boolean value, boolean fireEvents) {
        root.setValue(value, fireEvents);
    }
}