<commit_before>/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.model;

import static org.csstudio.display.builder.model.ModelPlugin.logger;

import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;

import org.csstudio.display.builder.model.persist.ModelReader;

/** Widget 'class' support
 *
 *  <p>Maintains 'classes' for each widget type,
 *  for example a 'TITLE' class for 'label' widgets.
 *  A class consists of properties and values
 *  which can then be applied to a widget.
 *
 *  <p>Each property of a widget can be configured
 *  to _not_ use the class settings.
 *
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class WidgetClassSupport
{
    /** File extension used for widget class definition files */
    // 'bob class file'
    public static final String FILE_EXTENSION = "bcf";

    /** Name of the default class
     *
     *  <p>Is always present, using the default value
     *  of each property.
     *  Can be overwritten in widget class file.
     */
    public static final String DEFAULT = "DEFAULT";

    /** Class-based value for a property
     *
     *  <p>Keeps either the specification (for macro-based)
     *  or the class-based value of property
     *  and supports applying it to a widget's property.
     */
    private static class ClassValue
    {
        private final String specification;
        private final Object value;

        public ClassValue(final WidgetProperty<?> property)
        {
            if (property instanceof MacroizedWidgetProperty)
            {
                specification = ((MacroizedWidgetProperty<?>) property).getSpecification();
                value = null;
            }
            else
            {
                specification = null;
                value = property.getValue();
            }
        }

        public void apply(final WidgetProperty<?> property)
        {
            if (specification != null)
            {
                if (property instanceof MacroizedWidgetProperty)
                    ((MacroizedWidgetProperty<?>)property).setSpecification(specification);
                else
                {
                    final Widget widget = property.getWidget();
                    logger.log(Level.WARNING, "Expected macroized property to apply class value for " + widget.getWidgetClass() +
                            " to property " + property.getName() + " of " + widget);
                }
            }
            else
                try
                {
                    property.setValueFromObject(value);
                }
                catch (Exception ex)
                {
                    final Widget widget = property.getWidget();
                    logger.log(Level.WARNING, "Cannot apply class value for " + widget.getWidgetClass() +
                                              " to property " + property.getName() + " of " + widget, ex);
                }
        }
    }

    /** Map:
     *  widget type to classes-for-type,
     *  class name to properties,
     *  property name to class value
     */
    // TreeMap  -> sorted; may help when dumping, debugging
    private final Map<String,
                      Map<String,
                          Map<String, ClassValue>>> widget_types = new TreeMap<>();

    /** Load widget classes
     *
     *  @param stream Stream for a widget class file
     *  @throws Exception on error
     */
    public WidgetClassSupport(final InputStream stream) throws Exception
    {
        // Register all DEFAULT widgets, including model itself
        DisplayModel model = new DisplayModel();
        model.propName().setValue(DEFAULT);
        registerClass(model);
        for (WidgetDescriptor descr : WidgetFactory.getInstance().getWidgetDescriptions())
        {
            final Widget widget = descr.createWidget();
            widget.propName().setValue(DEFAULT);
            registerClass(widget);
        }

        // Register widget classes from class definition file
        model = new ModelReader(stream).readModel();
        model.propName().setValue(DEFAULT);
        registerClass(model);
        for (Widget widget : model.getChildren())
            registerClass(widget);
    }

    /** @param widget Widget to register, using its class and properties */
    private void registerClass(final Widget widget)
    {
        final String type = widget.getType();
        // For the class definition file, the widget _name_ sets the class to define
        final String widget_class = widget.getName();
        final Map<String, Map<String, ClassValue>> widget_classes = widget_types.computeIfAbsent(type, t -> new TreeMap<>());
        final Map<String, ClassValue> class_properties = widget_classes.computeIfAbsent(widget_class, c -> new TreeMap<>());
        for (WidgetProperty<?> property : widget.getProperties())
            class_properties.put(property.getName(), new ClassValue(property));
    }

    /** Get known widget classes
     *  @param widget_type Widget type
     *  @return Widget class names for that type
     */
    public Collection<String> getWidgetClasses(final String widget_type)
    {
        final Map<String, Map<String, ClassValue>> classes = widget_types.get(widget_type);
        if (classes == null)
            return Arrays.asList(DEFAULT);
        return classes.keySet();
    }

    /** Get class-based properties
     *<commit_msg>Classes: Sort names, but keep 'DEFAULT' on top<commit_after>/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.model;

import static org.csstudio.display.builder.model.ModelPlugin.logger;

import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;

import org.csstudio.display.builder.model.persist.ModelReader;

/** Widget 'class' support
 *
 *  <p>Maintains 'classes' for each widget type,
 *  for example a 'TITLE' class for 'label' widgets.
 *  A class consists of properties and values
 *  which can then be applied to a widget.
 *
 *  <p>Each property of a widget can be configured
 *  to _not_ use the class settings.
 *
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class WidgetClassSupport
{
    /** File extension used for widget class definition files */
    // 'bob class file'
    public static final String FILE_EXTENSION = "bcf";

    /** Name of the default class
     *
     *  <p>Is always present, using the default value
     *  of each property.
     *  Can be overwritten in widget class file.
     */
    public static final String DEFAULT = "DEFAULT";

    /** Alphabetical sort with exception that 'DEFAULT' is on top */
    private static final Comparator<String> classname_sort = (a, b) ->
    {
        final int cmp = a.compareTo(b);
        if (cmp == 0)
            return 0;
        if (DEFAULT.equals(a))
            return -1;
        if (DEFAULT.equals(b))
            return 1;
        return cmp;
    };

    /** Class-based value for a property
     *
     *  <p>Keeps either the specification (for macro-based)
     *  or the class-based value of property
     *  and supports applying it to a widget's property.
     */
    private static class ClassValue
    {
        private final String specification;
        private final Object value;

        public ClassValue(final WidgetProperty<?> property)
        {
            if (property instanceof MacroizedWidgetProperty)
            {
                specification = ((MacroizedWidgetProperty<?>) property).getSpecification();
                value = null;
            }
            else
            {
                specification = null;
                value = property.getValue();
            }
        }

        public void apply(final WidgetProperty<?> property)
        {
            if (specification != null)
            {
                if (property instanceof MacroizedWidgetProperty)
                    ((MacroizedWidgetProperty<?>)property).setSpecification(specification);
                else
                {
                    final Widget widget = property.getWidget();
                    logger.log(Level.WARNING, "Expected macroized property to apply class value for " + widget.getWidgetClass() +
                            " to property " + property.getName() + " of " + widget);
                }
            }
            else
                try
                {
                    property.setValueFromObject(value);
                }
                catch (Exception ex)
                {
                    final Widget widget = property.getWidget();
                    logger.log(Level.WARNING, "Cannot apply class value for " + widget.getWidgetClass() +
                                              " to property " + property.getName() + " of " + widget, ex);
                }
        }
    }

    /** Map:
     *  widget type to classes-for-type,
     *  class name to properties,
     *  property name to class value
     */
    // TreeMap  -> sorted; may help when dumping, debugging
    private final Map<String,
                      Map<String,
                          Map<String, ClassValue>>> widget_types = new TreeMap<>();

    /** Load widget classes
     *
     *  @param stream Stream for a widget class file
     *  @throws Exception on error
     */
    public WidgetClassSupport(final InputStream stream) throws Exception
    {
        // Register all DEFAULT widgets, including model itself
        DisplayModel model = new DisplayModel();
        model.propName().setValue(DEFAULT);
        registerClass(model);
        for (WidgetDescriptor descr : WidgetFactory.getInstance().getWidgetDescriptions())
        {
            final Widget widget = descr.createWidget();
            widget.propName().setValue(DEFAULT);
            registerClass(widget);
        }

        // Register widget classes from class definition file
        model = new ModelReader(stream).readModel();
        model.propName().setValue(DEFAULT);
        registerClass(model);
        for (Widget widget : model.getChildren())
            registerClass(widget);
    }

    /** @param widget Widget to register, using its class and properties */
    private void registerClass(final Widget widget)
    {
        final String type = widget.getType();
        // For the class definition file, the widget _name_ sets the class to define
        final String widget_class = widget.getName();
        final Map<String, Map<String, ClassValue>> widget_classes = widget_types.computeIfAbsent(type, t -> new TreeMap<>(classname_sort));
        final Map<String, ClassValue> class_properties = widget_classes.computeIfAbsent(widget_class, c -> new TreeMap<>());
        for (WidgetProperty<?> property : widget.getProperties())
            class_properties.put(property.getName(), new ClassValue(property));
    }

    /** Get known widget classes
     *  @param widget_type Widget type
     *  @return Widget class names for that type
     */
    public Collection<String> getWidgetClasses(final String widget_type)
    {
        final Map<String, Map<String, ClassValue>> classes = widget_types.get(widget_type);
        if (classes == null)
            return Arrays.asList(DEFAULT);
        return classes.keySet();
    }

    /** Get class-based properties
     *
{'fraction_non_alphanumeric': 0.11460783518533539, 'fraction_numerical': 0.0022710682131559736, 'mean_word_length': 2.198443579766537, 'pattern_counts': {'":': 0, '<': 48, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6748911', 'n_tokens_mistral': 3050, 'n_tokens_neox': 2983, 'n_words': 950}