switch(e.keyCode) {
                    case SWT.DEL:
                        // delete tag/category only when the delete action is enabled
                        if (_action_DeleteTag.isEnabled() || _action_DeleteCategory.isEnabled()) {
                            onAction_DeleteTagOrCategory();
                        }
                        break;
                    case SWT.F2:
                        onAction_Edit_TagOrCategory();
                        break;
                }

package net.tourbook.preferences;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import net.tourbook.Images;
import net.tourbook.Messages;
import net.tourbook.application.TourbookPlugin;
import net.tourbook.common.UI;
import net.tourbook.common.util.ColumnDefinition;
import net.tourbook.common.util.ColumnManager;
import net.tourbook.common.util.ITourViewer;
import net.tourbook.common.util.ITreeViewer;
import net.tourbook.common.util.TreeViewerItem;
import net.tourbook.data.TourTag;
import net.tourbook.data.TourTagCategory;
import net.tourbook.database.TourDatabase;
import net.tourbook.tag.Dialog_TourTag;
import net.tourbook.tag.Dialog_TourTag_Category;
import net.tourbook.tag.TVIPrefTag;
import net.tourbook.tag.TVIPrefTagCategory;
import net.tourbook.tag.TVIPrefTagRoot;
import net.tourbook.tag.TagManager;
import net.tourbook.tag.TagMenuManager;
import net.tourbook.tour.TourEventId;
import net.tourbook.tour.TourLogManager;
import net.tourbook.tour.TourManager;
import net.tourbook.ui.action.ActionCollapseAll;
import net.tourbook.ui.action.ActionExpandSelection;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.dialogs.PreferencesUtil;

public class PrefPageTags extends PreferencePage implements IWorkbenchPreferencePage, ITourViewer, ITreeViewer {

    public static final String ID = "net.tourbook.preferences.PrefPageTags";

    private static final String SORT_PROPERTY = "sort";

    private final IPreferenceStore _prefStore = TourbookPlugin.getPrefStore();

    private IPropertyChangeListener _prefChangeListener;

    private TreeViewer _tagViewer;

    private TVIPrefTagRoot _rootItem;

    private ActionCollapseAll _action_CollapseAll;

    private Action_DeleteTag _action_DeleteTag;

    private Action_DeleteTagCategory _action_DeleteCategory;

    private ActionExpandSelection _action_ExpandSelection;

    private boolean _isMouseContextMenu;

    private boolean _isModified = false;

    private boolean _isSelectedWithKeyboard;

    private long _dragStartTime;

    /*
    * Image resources
    */
    private Image _imgTag = TourbookPlugin.getImageDescriptor(Images.Tag).createImage();

    private Image _imgTagRoot = TourbookPlugin.getImageDescriptor(Images.Tag_Root).createImage();

    private Image _imgTagCategory = TourbookPlugin.getImageDescriptor(Images.Tag_Category).createImage();

    /*
    * UI controls
    */
    private ToolBar _toolBar;

    private Button _btnEditTagOrCategory;

    private Button _btnNewTag;

    private Button _btnNewTagCategory;

    private Button _btnReset;

    private class Action_DeleteTag extends Action {

        Action_DeleteTag() {
            super(Messages.Action_Tag_Delete, AS_PUSH_BUTTON);
            setImageDescriptor(TourbookPlugin.getImageDescriptor(Images.App_Delete));
            setDisabledImageDescriptor(TourbookPlugin.getImageDescriptor(Images.App_Delete_Disabled));
        }

        @Override
        public void run() {
            onAction_DeleteTagOrCategory();
        }
    }

    private class Action_DeleteTagCategory extends Action {

        Action_DeleteTagCategory() {
            super(Messages.Action_Tag_DeleteCategory, AS_PUSH_BUTTON);
            setImageDescriptor(TourbookPlugin.getImageDescriptor(Images.App_Delete));
            setDisabledImageDescriptor(TourbookPlugin.getImageDescriptor(Images.App_Delete_Disabled));
        }

        @Override
        public void run() {
            onAction_DeleteTagOrCategory();
        }
    }

    /**
     * Sort the tags and categories
     */
    private final static class TagViewerComparator extends ViewerComparator {

        @Override
        public int compare(final Viewer viewer, final Object obj1, final Object obj2) {
            if (obj1 instanceof TVIPrefTag && obj2 instanceof TVIPrefTag) {
                // sort tags by name
                final TourTag tourTag1 = ((TVIPrefTag) (obj1)).getTourTag();
                final TourTag tourTag2 = ((TVIPrefTag) (obj2)).getTourTag();
                return tourTag1.getTagName().compareTo(tourTag2.getTagName());
            } else if (obj1 instanceof TVIPrefTag && obj2 instanceof TVIPrefTagCategory) {
                // sort category before tag
                return 1;
            } else if (obj2 instanceof TVIPrefTag && obj1 instanceof TVIPrefTagCategory) {
                // sort category before tag
                return -1;
            } else if (obj1 instanceof TVIPrefTagCategory && obj2 instanceof TVIPrefTagCategory) {
                // sort categories by name
                final TourTagCategory tourTagCat1 = ((TVIPrefTagCategory) (obj1)).getTourTagCategory();
                final TourTagCategory tourTagCat2 = ((TVIPrefTagCategory) (obj2)).getTourTagCategory();
                return tourTagCat1.getCategoryName().compareTo(tourTagCat2.getCategoryName());
            }
            return 0;
        }

        @Override
        public boolean isSorterProperty(final Object element, final String property) {
            // sort when the name has changed
            return true;
        }
    }

    /**
     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

     *

     * The comparer is necessary to set and restore the expanded elements

     *

     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

     */
    private class TagViewerComparer implements IElementComparer {

        @Override
        public boolean equals(final Object a, final Object b) {
            if (a == b) {
                return true;
            } else if (a instanceof TVIPrefTag && b instanceof TVIPrefTag) {
                return ((TVIPrefTag) a).getTourTag().getTagId() == ((TVIPrefTag) b).getTourTag().getTagId();
            } else if (a instanceof TVIPrefTagCategory && b instanceof TVIPrefTagCategory) {
                return ((TVIPrefTagCategory) a).getTourTagCategory().getCategoryId() == ((TVIPrefTagCategory) b).getTourTagCategory().getCategoryId();
            }
            return false;
        }

        @Override
        public int hashCode(final Object element) {
            return 0;
        }
    }

    private final class TagViewerContentProvider implements ITreeContentProvider {

        @Override
        public Object[] getChildren(final Object parentElement) {
            return ((TreeViewerItem) parentElement).getFetchedChildrenAsArray();
        }

        @Override
        public Object[] getElements(final Object inputElement) {
            return _rootItem.getFetchedChildrenAsArray();
        }

        @Override
        public Object getParent(final Object element) {
            return ((TreeViewerItem) element).getParentItem();
        }

        @Override
        public boolean hasChildren(final Object element) {
            return ((TreeViewerItem) element).hasChildren();
        }
    }

    public PrefPageTags() {
    }

    public PrefPageTags(final String title) {
        super(title);
    }

    public PrefPageTags(final String title, final ImageDescriptor image) {
        super(title, image);
    }

    private void addPrefListener() {
        _prefChangeListener = new IPropertyChangeListener() {

            @Override
            public void propertyChange(final PropertyChangeEvent event) {
                final String property = event.getProperty();
                if (property.equals(ITourbookPreferences.VIEW_LAYOUT_CHANGED)) {
                    _tagViewer.getTree().setLinesVisible(getPreferenceStore().getBoolean(ITourbookPreferences.VIEW_LAYOUT_DISPLAY_LINES));
                    _tagViewer.refresh();
                    /*
                * the tree must be redrawn because the styled text does not display the new color
                */
                    _tagViewer.getTree().redraw();
                }
            }
        };
        // register the listener
        _prefStore.addPropertyChangeListener(_prefChangeListener);
    }

    private void createActions() {
        _action_DeleteTag = new Action_DeleteTag();
        _action_DeleteCategory = new Action_DeleteTagCategory();
        _action_ExpandSelection = new ActionExpandSelection(this, true);
        _action_CollapseAll = new ActionCollapseAll(this);
    }

    @Override
    protected Control createContents(final Composite parent) {
        final Composite ui = createUI(parent);
        createActions();
        fillToolbar();
        updateTagViewer();
        enableControls();
        addPrefListener();
        return ui;
    }

    private Composite createUI(final Composite parent) {
        // container
        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
        GridLayoutFactory.fillDefaults().margins(0, 0).numColumns(2).applyTo(container);
        {
            createUI_10_Title(container);
            createUI_20_TagViewer(container);
            createUI_30_Buttons(container);
            createUI_40_Bottom(container);
        }
        // spacer
        new Label(parent, SWT.NONE);
        return container;
    }

    private void createUI_10_Title(final Composite parent) {
        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container);
        {
            // label: title
            final Label label = new Label(container, SWT.WRAP);
            label.setText(Messages.pref_tourtag_viewer_title);
            GridDataFactory.swtDefaults().grab(true, false).applyTo(label);
            // toolbar
            _toolBar = new ToolBar(container, SWT.FLAT);
        }
        // spacer
        new Label(parent, SWT.NONE);
    }

    private void createUI_20_TagViewer(final Composite parent) {
        /*
       * create tree layout
       */
        final Composite layoutContainer = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, true).hint(400, 500).applyTo(layoutContainer);
        final TreeColumnLayout treeLayout = new TreeColumnLayout();
        layoutContainer.setLayout(treeLayout);
        /*
       * create viewer
       */
        final Tree tree = new Tree(layoutContainer, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
        tree.setHeaderVisible(true);
        tree.setLinesVisible(getPreferenceStore().getBoolean(ITourbookPreferences.VIEW_LAYOUT_DISPLAY_LINES));
        tree.addListener(SWT.MouseDoubleClick, event -> onTagTree_DoubleClick(event));
        tree.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(final KeyEvent e) {
                _isSelectedWithKeyboard = true;

            }

            @Override
            public void keyReleased(final KeyEvent e) {
            }
        });
        tree.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseDown(final MouseEvent e) {
                _isMouseContextMenu = e.button == 3;
            }
        });
        _tagViewer = new TreeViewer(tree);
        _tagViewer.setContentProvider(new TagViewerContentProvider());
        _tagViewer.setComparator(new TagViewerComparator());
        _tagViewer.setComparer(new TagViewerComparer());
        _tagViewer.setUseHashlookup(true);
        _tagViewer.addDoubleClickListener(new IDoubleClickListener() {

            @Override
            public void doubleClick(final DoubleClickEvent event) {
                onTagViewer_DoubleClick();
            }
        });
        _tagViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                onTagViewer_Selection(event);
            }
        });
        _tagViewer.addDragSupport(DND.DROP_MOVE, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DragSourceListener() {

            @Override
            public void dragFinished(final DragSourceEvent event) {
                final LocalSelectionTransfer transfer = LocalSelectionTransfer.getTransfer();
                if (event.doit == false) {
                    return;
                }
                transfer.setSelection(null);
                transfer.setSelectionSetTime(0);
            }

            @Override
            public void dragSetData(final DragSourceEvent event) {
                // data are set in LocalSelectionTransfer
            }

            @Override
            public void dragStart(final DragSourceEvent event) {
                final LocalSelectionTransfer transfer = LocalSelectionTransfer.getTransfer();
                final ISelection selection = _tagViewer.getSelection();
                transfer.setSelection(selection);
                transfer.setSelectionSetTime(_dragStartTime = event.time & 0xFFFFFFFFL);
                event.doit = !selection.isEmpty();
            }
        });
        _tagViewer.addDropSupport(DND.DROP_MOVE, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new TagDropAdapter(this, _tagViewer));
        defineAllColumns(treeLayout);
        createUI_22_ContextMenu(tree);
    }

    private void createUI_22_ContextMenu(final Tree tree) {
        final MenuManager menuManager = new MenuManager();
        menuManager.setRemoveAllWhenShown(true);
        menuManager.addMenuListener(new IMenuListener() {

            @Override
            public void menuAboutToShow(final IMenuManager manager) {
                fillContextMenu(manager);
                enableControls();
            }
        });
        tree.setMenu(menuManager.createContextMenu(tree));
    }

    private void createUI_30_Buttons(final Composite parent) {
        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().indent(5, 0).applyTo(container);
        GridLayoutFactory.fillDefaults().applyTo(container);
        {
            {
                _btnNewTag = new Button(container, SWT.NONE);
                _btnNewTag.setText(Messages.pref_tourtag_btn_new_tag);
                _btnNewTag.addSelectionListener(new SelectionAdapter() {

                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onAction_NewTag();
                    }
                });
                GridDataFactory.fillDefaults().grab(true, false).applyTo(_btnNewTag);
            }
            {
                // Button: new tag category
                _btnNewTagCategory = new Button(container, SWT.NONE);
                _btnNewTagCategory.setText(Messages.pref_tourtag_btn_new_tag_category);
                _btnNewTagCategory.addSelectionListener(new SelectionAdapter() {

                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onAction_NewCategory();
                    }
                });
                GridDataFactory.fillDefaults().grab(true, false).applyTo(_btnNewTagCategory);
            }
            {
                // Button: edit tag or category
                _btnEditTagOrCategory = new Button(container, SWT.NONE);
                _btnEditTagOrCategory.setText(Messages.Action_TagCategory_EditCategory);
                _btnEditTagOrCategory.addSelectionListener(new SelectionAdapter() {

                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onAction_Edit_TagOrCategory();
                    }
                });
                GridDataFactory.fillDefaults().grab(true, false).applyTo(_btnEditTagOrCategory);
            }
            {
                // Button: reset
                _btnReset = new Button(container, SWT.NONE);
                _btnReset.setText(Messages.pref_tourtag_btn_reset);
                _btnReset.addSelectionListener(new SelectionAdapter() {

                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onAction_Reset();
                    }
                });
                GridDataFactory.fillDefaults().grab(true, false).indent(0, 50).applyTo(_btnReset);
            }
        }
    }

    private void createUI_40_Bottom(final Composite parent) {
        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(container);
        {
            final Label label = new Label(container, SWT.WRAP);
            label.setText(Messages.pref_tourtag_hint);
            GridDataFactory.swtDefaults().grab(true, false).applyTo(label);
            final Link link = new Link(container, SWT.WRAP);
            GridDataFactory.swtDefaults().grab(true, false).applyTo(link);
            link.setText(Messages.Pref_TourTag_Link_AppearanceOptions);
            link.addSelectionListener(new SelectionAdapter() {

                @Override
                public void widgetSelected(final SelectionEvent e) {
                    PreferencesUtil.createPreferenceDialogOn(getShell(), PrefPageAppearance.ID, null, null);
                }
            });
        }
    }

    private void defineAllColumns(final TreeColumnLayout treeLayout) {
        TreeViewerColumn tvc;
        TreeColumn tvcColumn;
        {
            // column: tags + tag categories
            tvc = new TreeViewerColumn(_tagViewer, SWT.LEAD);
            tvcColumn = tvc.getColumn();
            tvcColumn.setText(Messages.Pref_TourTag_Column_TagsAndCategories);
            tvc.setLabelProvider(new StyledCellLabelProvider() {

                @Override
                public void update(final ViewerCell cell) {
                    final StyledString styledString = new StyledString();
                    final Object element = cell.getElement();
                    if (element instanceof TVIPrefTag) {
                        final TourTag tourTag = ((TVIPrefTag) element).getTourTag();
                        String tagName = tourTag.getTagName();
                        if (UI.IS_SCRAMBLE_DATA) {
                            tagName = UI.scrambleText(tagName);
                        }
                        styledString.append(tagName, net.tourbook.ui.UI.TAG_STYLER);
                        cell.setImage(tourTag.isRoot() ? _imgTagRoot : _imgTag);
                    } else if (element instanceof TVIPrefTagCategory) {
                        final TVIPrefTagCategory tourTagCategoryItem = (TVIPrefTagCategory) element;
                        final TourTagCategory tourTagCategory = tourTagCategoryItem.getTourTagCategory();
                        cell.setImage(_imgTagCategory);
                        String categoryName = tourTagCategory.getCategoryName();
                        if (UI.IS_SCRAMBLE_DATA) {
                            categoryName = UI.scrambleText(categoryName);
                        }
                        styledString.append(categoryName, net.tourbook.ui.UI.TAG_CATEGORY_STYLER);
                        // get number of categories
                        final int categoryCounter = tourTagCategory.getCategoryCounter();
                        final int tagCounter = tourTagCategory.getTagCounter();
                        if (categoryCounter == -1 && tagCounter == -1) {
                        } else {
                            String categoryString = UI.EMPTY_STRING;
                            if (categoryCounter > 0) {
                                categoryString = "/" + categoryCounter;
                            }
                            styledString.append("   " + tagCounter + categoryString, StyledString.QUALIFIER_STYLER);
                        }
                    } else {
                        styledString.append(element.toString());
                    }
                    cell.setText(styledString.getString());
                    cell.setStyleRanges(styledString.getStyleRanges());
                }
            });
            treeLayout.setColumnData(tvcColumn, new ColumnWeightData(100, true));
        }
        {
            // column: notes
            tvc = new TreeViewerColumn(_tagViewer, SWT.LEAD);
            tvcColumn = tvc.getColumn();
            tvcColumn.setText(Messages.Pref_TourTag_Column_Notes);
            tvc.setLabelProvider(new CellLabelProvider() {

                @Override
                public void update(final ViewerCell cell) {
                    String notes = UI.EMPTY_STRING;
                    final Object element = cell.getElement();
                    if (element instanceof TVIPrefTag) {
                        final TourTag tourTag = ((TVIPrefTag) element).getTourTag();
                        notes = tourTag.getNotes();
                    } else if (element instanceof TVIPrefTagCategory) {
                        final TourTagCategory tourTagCategory = ((TVIPrefTagCategory) element).getTourTagCategory();
                        notes = tourTagCategory.getNotes();
                    }
                    final String shortedNotes = UI.shortenText(notes, 200, true);
                    cell.setText(shortedNotes);
                }
            });
            treeLayout.setColumnData(tvcColumn, new ColumnWeightData(80, true));
        }
    }

    @Override
    public void dispose() {
        _imgTag.dispose();
        _imgTagRoot.dispose();
        _imgTagCategory.dispose();
        _prefStore.removePropertyChangeListener(_prefChangeListener);
        super.dispose();
    }

    private void doLiveUpdate() {
        _isModified = true;
        fireModifyEvent();
    }

    private void enableControls() {
        final IStructuredSelection selection = _tagViewer.getStructuredSelection();
        final Object firstElement = selection.getFirstElement();
        /*
       * Count number of selected tours/tags/categories
       */
        int numTags = 0;
        int numCategorys = 0;
        int numOtherItems = 0;
        for (Object treeItem : selection) {
            if (treeItem instanceof TVIPrefTag) {
                numTags++;
            } else if (treeItem instanceof TVIPrefTagCategory) {
                numCategorys++;
            } else {
                numOtherItems++;
            }
        }
        final boolean isTagSelected = numTags > 0 && numCategorys == 0 && numOtherItems == 0;
        final boolean isCategorySelected = numCategorys == 1 && numTags == 0 && numOtherItems == 0;
        final boolean isSelection = firstElement != null;
        _btnNewTag.setEnabled((isSelection == false || isCategorySelected && isTagSelected == false));
        _btnNewTagCategory.setEnabled((isSelection == false || isCategorySelected && isTagSelected == false));
        final boolean isOneItemSelected = selection.size() == 1;
        if (isOneItemSelected) {
            if (isTagSelected) {
                _btnEditTagOrCategory.setText(Messages.Action_Tag_Edit);
                _btnEditTagOrCategory.setEnabled(true);
            } else if (isCategorySelected) {
                _btnEditTagOrCategory.setText(Messages.Action_TagCategory_EditCategory);
                _btnEditTagOrCategory.setEnabled(true);
            }
        } else {
            _btnEditTagOrCategory.setEnabled(false);
        }
        _btnReset.setEnabled(true);
        _tagViewer.getTree().setEnabled(true);
        _action_CollapseAll.setEnabled(true);
        _action_ExpandSelection.setEnabled(true);
        _action_DeleteTag.setEnabled(isTagSelected);
        _action_DeleteCategory.setEnabled(isOneItemSelected && isCategorySelected);
    }

    private void fillContextMenu(final IMenuManager menuMgr) {
        menuMgr.add(_action_CollapseAll);
        menuMgr.add(_action_ExpandSelection);
        menuMgr.add(new Separator());
        menuMgr.add(_action_DeleteTag);
        menuMgr.add(_action_DeleteCategory);
    }

    /**
     * set the toolbar action after the {@link #_tagViewer} is created
     */
    private void fillToolbar() {
        final ToolBarManager tbm = new ToolBarManager(_toolBar);
        tbm.add(_action_ExpandSelection);
        tbm.add(_action_CollapseAll);
        tbm.update(true);
    }

    private void fireModifyEvent() {
        if (_isModified) {
            _isModified = false;
            // remove old tags from cached tours
            TourDatabase.clearTourTags();
            TagMenuManager.updateRecentTagNames();
            TourManager.getInstance().clearTourDataCache();
            // fire modify event
            TourManager.fireEvent(TourEventId.TAG_STRUCTURE_CHANGED);
        }
    }

    @Override
    public ColumnManager getColumnManager() {
        return null;
    }

    public long getDragStartTime() {
        return _dragStartTime;
    }

    public TVIPrefTagRoot getRootItem() {
        return _rootItem;
    }

    @Override
    public TreeViewer getTreeViewer() {
        return _tagViewer;
    }

    @Override
    public ColumnViewer getViewer() {
        return _tagViewer;
    }

    @Override
    public void init(final IWorkbench workbench) {
        setPreferenceStore(_prefStore);
    }

    @Override
    public boolean isValid() {
        return true;
    }

    private void onAction_DeleteTagOrCategory() {
        final ITreeSelection structuredSelection = _tagViewer.getStructuredSelection();
        final Object firstSelectedItem = structuredSelection.getFirstElement();
        if (firstSelectedItem instanceof TVIPrefTag) {
            // delete tag
            final List<?> allSelection = structuredSelection.toList();
            final HashMap<Long, TourTag> allTourTags = TourDatabase.getAllTourTags();
            final ArrayList<TourTag> allSelectedTags = new ArrayList<>();
            for (final Object object : allSelection) {
                if (object instanceof TVIPrefTag) {
                    final TVIPrefTag tviTag = (TVIPrefTag) object;
                    allSelectedTags.add(allTourTags.get(tviTag.getTourTag().getTagId()));
                }
            }
            if (allSelectedTags.size() > 0) {
                if (TagManager.deleteTourTag(allSelectedTags)) {
                    // update tag viewer with new loaded structure
                    updateTagViewer();
                }
            }
        } else if (firstSelectedItem instanceof TVIPrefTagCategory) {
            // delete category
            final TourTagCategory tourTagCategory = ((TVIPrefTagCategory) firstSelectedItem).getTourTagCategory();
            if (TagManager.deleteTourTagCategory(tourTagCategory.getCategoryId(), tourTagCategory.getCategoryName())) {
                updateTagViewer();
            }
        }
        setFocusToViewer();
    }

    /**
     * Edit selected tag/category
     */
    private void onAction_Edit_TagOrCategory() {
        final Object firstElement = _tagViewer.getStructuredSelection().getFirstElement();
        String dlgMessage = UI.EMPTY_STRING;
        if (firstElement instanceof TVIPrefTag) {
            final TVIPrefTag tourTagItem = ((TVIPrefTag) firstElement);
            final TourTag tourTag = tourTagItem.getTourTag();
            dlgMessage = NLS.bind(Messages.Dialog_TourTag_EditTag_Message, tourTag.getTagName());
            if (new Dialog_TourTag(getShell(), dlgMessage, tourTag).open() != Window.OK) {
                setFocusToViewer();
                return;
            }
            // update model
            TourDatabase.saveEntity(tourTag, tourTag.getTagId(), TourTag.class);
            // update UI
            _tagViewer.update(tourTagItem, new String[] { SORT_PROPERTY });
        } else if (firstElement instanceof TVIPrefTagCategory) {
            final TVIPrefTagCategory tagCategoryItem = (TVIPrefTagCategory) firstElement;
            final TourTagCategory tourTagCategory = tagCategoryItem.getTourTagCategory();
            dlgMessage = NLS.bind(Messages.Dialog_TourTagCategory_EditCategory_Message, tourTagCategory.getCategoryName());
            if (new Dialog_TourTag_Category(getShell(), dlgMessage, tourTagCategory).open() != Window.OK) {
                setFocusToViewer();
                return;
            }
            // update model
            TourDatabase.saveEntity(tourTagCategory, tourTagCategory.getCategoryId(), TourTagCategory.class);
            // update UI
            _tagViewer.update(tagCategoryItem, new String[] { SORT_PROPERTY });
        } else {
            return;
        }
        _isModified = true;
        setFocusToViewer();
    }

    private void onAction_NewCategory() {
        final InputDialog inputDialog = new InputDialog(getShell(), Messages.pref_tourtag_dlg_new_tag_category_title, Messages.pref_tourtag_dlg_new_tag_category_message, UI.EMPTY_STRING, null);
        if (inputDialog.open() != Window.OK) {
            setFocusToViewer();
            return;
        }
        // create tag category + tree item
        final TourTagCategory newCategory = new TourTagCategory(inputDialog.getValue().trim());
        final TVIPrefTagCategory newCategoryItem = new TVIPrefTagCategory(_tagViewer, newCategory);
        final Object parentElement = ((StructuredSelection) _tagViewer.getSelection()).getFirstElement();
        TourTagCategory savedNewCategory = null;
        if (parentElement == null) {
            // a parent is not selected, this will be a root category
            newCategory.setRoot(true);
            /*
          * Update model
          */
            _rootItem.getFetchedChildren().add(newCategoryItem);
            // persist new category
            savedNewCategory = TourDatabase.saveEntity(newCategory, newCategory.getCategoryId(), TourTagCategory.class);
            if (savedNewCategory != null) {
                // update item
                newCategoryItem.setTourTagCategory(savedNewCategory);
                /*
             * Update UI
             */
                _tagViewer.add(_rootItem, newCategoryItem);
            }
        } else if (parentElement instanceof TVIPrefTagCategory) {
            // parent is a category
            final TVIPrefTagCategory parentCategoryItem = (TVIPrefTagCategory) parentElement;
            final TourTagCategory parentCategory = parentCategoryItem.getTourTagCategory();
            /*
          * update model
          */
            final EntityManager em = TourDatabase.getInstance().getEntityManager();
            // persist new category
            savedNewCategory = TourDatabase.saveEntity(newCategory, newCategory.getCategoryId(), TourTagCategory.class);
            if (savedNewCategory != null) {
                // update item
                newCategoryItem.setTourTagCategory(savedNewCategory);
                /*
             * Update parent category
             */
                final TourTagCategory parentCategoryEntity = em.find(TourTagCategory.class, parentCategory.getCategoryId());
                // set tag in parent category
                final Set<TourTagCategory> lazyTourTagCategories = parentCategoryEntity.getTagCategories();
                lazyTourTagCategories.add(savedNewCategory);
                // update number of categories
                parentCategoryEntity.setCategoryCounter(lazyTourTagCategories.size());
                /*
             * Persist parent category
             */
                final TourTagCategory savedParentCategory = TourDatabase.saveEntity(parentCategoryEntity, parentCategoryEntity.getCategoryId(), TourTagCategory.class);
                if (savedParentCategory != null) {
                    // update item
                    parentCategoryItem.setTourTagCategory(savedParentCategory);
                    /*
                * Update UI
                */
                    parentCategoryItem.clearChildren();
                    _tagViewer.add(parentCategoryItem, newCategoryItem);
                    _tagViewer.expandToLevel(parentCategoryItem, 1);
                }
            }
            em.close();
        }
        if (savedNewCategory != null) {
            // reveal new tag in viewer
            _tagViewer.reveal(newCategoryItem);
            _isModified = true;
        }
        setFocusToViewer();
    }

    private void onAction_NewTag() {
        final InputDialog inputDialog = new InputDialog(getShell(), Messages.pref_tourtag_dlg_new_tag_title, Messages.pref_tourtag_dlg_new_tag_message, UI.EMPTY_STRING, null);
        if (inputDialog.open() != Window.OK) {
            setFocusToViewer();
            return;
        }
        TourTag savedTag = null;
        // create new tour tag + item
        final TourTag tourTag = new TourTag(inputDialog.getValue().trim());
        final TVIPrefTag tagItem = new TVIPrefTag(_tagViewer, tourTag);
        final Object parentItem = ((StructuredSelection) _tagViewer.getSelection()).getFirstElement();
        if (parentItem == null) {
            // a parent is not selected, this will be a root tag
            tourTag.setRoot(true);
            /*
          * Update model
          */
            tagItem.setParentItem(_rootItem);
            _rootItem.getFetchedChildren().add(tagItem);
            // persist tag
            savedTag = TourDatabase.saveEntity(tourTag, TourDatabase.ENTITY_IS_NOT_SAVED, TourTag.class);
            if (savedTag != null) {
                // update item
                tagItem.setTourTag(savedTag);
                /*
             * Update UI
             */
                _tagViewer.add(_rootItem, tagItem);
            }
        } else if (parentItem instanceof TVIPrefTagCategory) {
            // parent is a category
            final TVIPrefTagCategory parentCategoryItem = (TVIPrefTagCategory) parentItem;
            TourTagCategory parentTagCategory = parentCategoryItem.getTourTagCategory();
            /*
          * update model
          */
            // set parent into tag
            tagItem.setParentItem(parentCategoryItem);
            /*
          * persist tag without new category otherwise an exception "detached entity passed to
          * persist: net.tourbook.data.TourTagCategory" is raised
          */
            savedTag = TourDatabase.saveEntity(tourTag, TourDatabase.ENTITY_IS_NOT_SAVED, TourTag.class);
            if (savedTag != null) {
                // update item
                tagItem.setTourTag(savedTag);
                // update parent category
                final EntityManager em = TourDatabase.getInstance().getEntityManager();
                {
                    final TourTagCategory parentTagCategoryEntity = em.find(TourTagCategory.class, parentTagCategory.getCategoryId());
                    // set new entity
                    parentTagCategory = parentTagCategoryEntity;
                    parentCategoryItem.setTourTagCategory(parentTagCategoryEntity);
                    // set tag into parent category
                    final Set<TourTag> lazyTourTags = parentTagCategoryEntity.getTourTags();
                    lazyTourTags.add(tourTag);
                    parentTagCategory.setTagCounter(lazyTourTags.size());
                }
                em.close();
                // persist parent category
                final TourTagCategory savedParent = TourDatabase.saveEntity(parentTagCategory, parentTagCategory.getCategoryId(), TourTagCategory.class);
                if (savedParent != null) {
                    // update item
                    parentCategoryItem.setTourTagCategory(savedParent);
                    // set category in tag,
                    // this seems to be not necessary
                    // persist tag with category
                    savedTag = TourDatabase.saveEntity(tourTag, tourTag.getTagId(), TourTag.class);
                }
            }
            if (savedTag != null) {
                // clear tour tag list
                TourDatabase.clearTourTags();
                /*
             * update viewer
             */
                parentCategoryItem.clearChildren();
                _tagViewer.add(parentCategoryItem, tagItem);
                _tagViewer.update(parentCategoryItem, null);
                _tagViewer.expandToLevel(parentCategoryItem, 1);
            }
        }
        if (savedTag != null) {
            // show new tag in viewer
            _tagViewer.reveal(tagItem);
            _isModified = true;
        }
        setFocusToViewer();
    }

    private void onAction_Reset() {
        final MessageDialog dialog = new MessageDialog(Display.getCurrent().getActiveShell(), Messages.pref_tourtag_dlg_reset_title, null, Messages.pref_tourtag_dlg_reset_message, MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 1);
        if (dialog.open() != Window.OK) {
            setFocusToViewer();
            return;
        }
        try (Connection conn = TourDatabase.getInstance().getConnection()) {
            TourLogManager.showLogView();
            TourLogManager.log_TITLE("RESET TAG STRUCTURE");
            {
                String sql;
                int result;
                // remove join table tag->category
                final Statement stmt1 = conn.createStatement();
                {
                    sql = "DELETE FROM " + TourDatabase.JOINTABLE__TOURTAGCATEGORY_TOURTAG;
                    result = stmt1.executeUpdate(sql);
                    TourLogManager.log_INFO("Deleted " + result + " entries from " + TourDatabase.JOINTABLE__TOURTAGCATEGORY_TOURTAG);
                }
                stmt1.close();
                // remove jointable category<->category
                final Statement stmt2 = conn.createStatement();
                {
                    sql = "DELETE FROM " + TourDatabase.JOINTABLE__TOURTAGCATEGORY_TOURTAGCATEGORY;
                    result = stmt2.executeUpdate(sql);
                    TourLogManager.log_INFO("Deleted " + result + " entries from " + TourDatabase.JOINTABLE__TOURTAGCATEGORY_TOURTAGCATEGORY);
                }
                stmt2.close();
                // set tags to root
                final Statement stmt3 = conn.createStatement();
                {
                    sql = "UPDATE " + TourDatabase.TABLE_TOUR_TAG + " SET isRoot=1";
                    result = stmt3.executeUpdate(sql);
                    TourLogManager.log_INFO("Set " + result + " tour tags to root");
                }
                stmt3.close();
                // set categories to root
                final Statement stmt4 = conn.createStatement();
                {
                    sql = "UPDATE " + TourDatabase.TABLE_TOUR_TAG_CATEGORY + " SET isRoot=1";
                    result = stmt4.executeUpdate(sql);
                    TourLogManager.log_INFO("Set " + result + " tour categories to root");
                }
                stmt4.close();
            }
            // update tag viewer with new loaded structure
            updateTagViewer();
            doLiveUpdate();
        } catch (final SQLException e) {
            net.tourbook.ui.UI.showSQLException(e);
        }
        setFocusToViewer();
    }

    private void onTagTree_DoubleClick(final Event event) {
        final boolean isCtrl = (event.stateMask & SWT.CTRL) != 0;
        if (isCtrl) {
            onAction_Edit_TagOrCategory();
        }
    }

    private void onTagViewer_DoubleClick() {
        final Object selection = ((IStructuredSelection) _tagViewer.getSelection()).getFirstElement();
        if (selection instanceof TVIPrefTag) {
            // tag is selected
            onAction_Edit_TagOrCategory();
        } else if (selection instanceof TVIPrefTagCategory) {
            // expand/collapse current item
            final TreeViewerItem tourItem = (TreeViewerItem) selection;
            if (_tagViewer.getExpandedState(tourItem)) {
                _tagViewer.collapseToLevel(tourItem, 1);
            } else {
                _tagViewer.expandToLevel(tourItem, 1);
            }
        }
    }

    private void onTagViewer_Selection(final SelectionChangedEvent event) {
        if (_isMouseContextMenu) {
            return;
        }
        final Object selectedItem = ((IStructuredSelection) (event.getSelection())).getFirstElement();
        if (selectedItem instanceof TVIPrefTag) {
            // a tag is selected
        } else if (selectedItem instanceof TVIPrefTagCategory) {
            // expand/collapse category
            if (_isSelectedWithKeyboard == false) {
                if (_tagViewer.getExpandedState(selectedItem)) {
                    _tagViewer.collapseToLevel(selectedItem, 1);
                } else {
                    _tagViewer.expandToLevel(selectedItem, 1);
                }
            }
        }
        // reset state
        _isSelectedWithKeyboard = false;
        enableControls();
    }

    @Override
    public boolean performCancel() {
        fireModifyEvent();
        return true;
    }

    @Override
    public boolean performOk() {
        fireModifyEvent();
        return true;
    }

    @Override
    public ColumnViewer recreateViewer(final ColumnViewer columnViewer) {
        return null;
    }

    @Override
    public void reloadViewer() {
    }

    private void setFocusToViewer() {
        // set focus back to the tree
        _tagViewer.getTree().setFocus();
    }

    public void setIsModified() {
        _isModified = true;
    }

    @Override
    public void updateColumnHeader(final ColumnDefinition colDef) {
    }

    /**
     * Update tag viewer with new loaded structure
     */
    private void updateTagViewer() {
        final Tree tree = _tagViewer.getTree();
        tree.setRedraw(false);
        {
            final Object[] expandedElements = _tagViewer.getExpandedElements();
            _tagViewer.setInput(_rootItem = new TVIPrefTagRoot(_tagViewer, true));
            _tagViewer.setExpandedElements(expandedElements);
        }
        tree.setRedraw(true);
        enableControls();
    }
}


switch(message.severity) {
                case ERROR:
                    errors.add(messageTuple);
                    break;
                case NOTE:
                    notes.add(messageTuple);
                    break;
                case WARNING:
                    warnings.add(messageTuple);
                    break;
            }

package org.metaborg.spoofax.meta.core.stratego.primitive;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.vfs2.AllFileSelector;
import org.apache.commons.vfs2.FileObject;
import org.metaborg.core.MetaborgException;
import org.metaborg.core.build.paths.ILanguagePathService;
import org.metaborg.core.config.ConfigException;
import org.metaborg.core.config.IExportConfig;
import org.metaborg.core.config.IExportVisitor;
import org.metaborg.core.config.LangDirExport;
import org.metaborg.core.config.LangFileExport;
import org.metaborg.core.config.ResourceExport;
import org.metaborg.core.context.IContext;
import org.metaborg.core.language.ILanguageComponent;
import org.metaborg.core.language.ILanguageImpl;
import org.metaborg.core.language.ILanguageService;
import org.metaborg.core.language.LanguageIdentifier;
import org.metaborg.core.project.IProject;
import org.metaborg.core.project.NameUtil;
import org.metaborg.core.resource.IResourceService;
import org.metaborg.spoofax.core.SpoofaxConstants;
import org.metaborg.spoofax.core.dynamicclassloading.DynamicClassLoadingFacet;
import org.metaborg.spoofax.core.stratego.primitive.generic.ASpoofaxContextPrimitive;
import org.metaborg.spoofax.meta.core.build.SpoofaxLangSpecCommonPaths;
import org.metaborg.spoofax.meta.core.config.ISpoofaxLanguageSpecConfig;
import org.metaborg.spoofax.meta.core.config.SpoofaxLanguageSpecConfig;
import org.metaborg.spoofax.meta.core.pluto.build.main.GenerateSourcesBuilder;
import org.metaborg.spoofax.meta.core.pluto.build.main.IPieProvider;
import org.metaborg.spoofax.meta.core.project.ISpoofaxLanguageSpec;
import org.metaborg.spoofax.meta.core.project.ISpoofaxLanguageSpecService;
import org.metaborg.util.cmd.Arguments;
import org.metaborg.util.log.ILogger;
import org.metaborg.util.log.LoggerUtils;
import org.spoofax.interpreter.stratego.Strategy;
import org.spoofax.interpreter.terms.IStrategoAppl;
import org.spoofax.interpreter.terms.IStrategoList;
import org.spoofax.interpreter.terms.IStrategoString;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.interpreter.terms.IStrategoTuple;
import org.spoofax.interpreter.terms.ITermFactory;
import org.spoofax.jsglr.client.imploder.ImploderAttachment;
import org.spoofax.terms.util.B;
import org.spoofax.terms.util.TermUtils;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.google.inject.Provider;
import mb.pie.api.ExecException;
import mb.pie.api.MixedSession;
import mb.pie.api.Pie;
import mb.pie.api.STask;
import mb.pie.api.Supplier;
import mb.pie.api.Task;
import mb.pie.api.TopDownSession;
import mb.pie.api.ValueSupplier;
import mb.resource.ResourceKey;
import mb.resource.fs.FSPath;
import mb.resource.hierarchical.ResourcePath;
import mb.stratego.build.strincr.IModuleImportService;
import mb.stratego.build.strincr.ModuleIdentifier;
import mb.stratego.build.strincr.Stratego2LibInfo;
import mb.stratego.build.strincr.message.Message;
import mb.stratego.build.strincr.task.CheckOpenModule;
import mb.stratego.build.strincr.task.input.CheckModuleInput;
import mb.stratego.build.strincr.task.input.FrontInput;
import mb.stratego.build.strincr.task.output.CheckOpenModuleOutput;
import mb.stratego.build.util.LastModified;

public class StrategoPieAnalyzePrimitive extends ASpoofaxContextPrimitive implements AutoCloseable {

    private static final ILogger logger = LoggerUtils.logger(StrategoPieAnalyzePrimitive.class);

    @Inject
    private static Provider<ISpoofaxLanguageSpecService> languageSpecServiceProvider;

    @Inject
    private static Provider<ILanguageService> languageServiceProvider;

    @Inject
    private static Provider<IPieProvider> pieProviderProvider;

    // Using provider to break cycle between Check -> Stratego runtime -> all primitives -> this primitive
    private final Provider<CheckOpenModule> checkModuleProvider;

    private final ILanguagePathService languagePathService;

    private final IResourceService resourceService;

    @Inject
    public StrategoPieAnalyzePrimitive(Provider<CheckOpenModule> checkModuleProvider, ILanguagePathService languagePathService, IResourceService resourceService) {
        super("stratego_pie_analyze", 0, 0);
        this.checkModuleProvider = checkModuleProvider;
        this.languagePathService = languagePathService;
        this.resourceService = resourceService;
    }

    @Override
    @Nullable
    protected IStrategoTerm call(IStrategoTerm current, Strategy[] svars, IStrategoTerm[] tvars, ITermFactory factory, IContext context) throws MetaborgException, IOException {
        final IStrategoAppl ast = TermUtils.toApplAt(current, 0);
        final String path = TermUtils.toJavaStringAt(current, 1);
        final String moduleName;
        if (!(ast.getName().equals("Module") && ast.getSubtermCount() == 2)) {
            moduleName = path;
        } else {
            moduleName = TermUtils.toJavaStringAt(ast, 0);
        }
        final IProject project = context.project();
        if (project == null) {
            logger.warn("Cannot find project for opened file, cancelling analysis. ");
            return null;
        }
        if (languageSpecServiceProvider == null || languageServiceProvider == null || pieProviderProvider == null) {
            // Indicates that meta-Spoofax is not available (ISpoofaxLanguageSpecService cannot be injected), but this
            // should never happen because this primitive is inside meta-Spoofax. Check for null just in case.
            logger.error("Spoofax meta services is not available; static injection failed");
            return null;
        }
        @Nullable
        final ISpoofaxLanguageSpec languageSpec = getLanguageSpecification(project);
        if (languageSpec == null) {
            logger.warn("Cannot find language specification for project of opened file, cancelling analysis. ");
            return null;
        }
        final ISpoofaxLanguageSpecConfig config = languageSpec.config();
        final FileObject baseLoc = languageSpec.location();
        final SpoofaxLangSpecCommonPaths paths = new SpoofaxLangSpecCommonPaths(baseLoc);
        String strMainModule = NameUtil.toJavaId(config.strategoName().toLowerCase());
        final Iterable<FileObject> strRoots = languagePathService.sourcePaths(project, SpoofaxConstants.LANG_STRATEGO_NAME);
        @Nullable
        File strMainFile;
        final FileObject strMainFileCandidate = config.strVersion().findStrMainFile(paths, strRoots, config.strategoName());
        if (strMainFileCandidate != null && strMainFileCandidate.exists()) {
            strMainFile = resourceService.localPath(strMainFileCandidate);
            if (strMainFile == null || !strMainFile.exists()) {
                logger.info("Main Stratego2 file at " + strMainFile + " does not exist");
                strMainFile = resourceService.localFile(resourceService.resolve(baseLoc, path));
                strMainModule = moduleName;
            }
        } else {
            logger.info("Main Stratego2 file with name " + strMainModule + ".str2 does not exist");
            strMainFile = resourceService.localFile(resourceService.resolve(baseLoc, path));
            strMainModule = moduleName;
        }
        final File strFile = resourceService.localFile(resourceService.resolve(baseLoc, path));
        @Nullable
        final String strExternalJarFlags = config.strExternalJarFlags();
        final Iterable<FileObject> strIncludePaths = Iterables.concat(languagePathService.sourceAndIncludePaths(languageSpec, SpoofaxConstants.LANG_STRATEGO_NAME), languagePathService.sourceAndIncludePaths(languageSpec, SpoofaxConstants.LANG_STRATEGO2_NAME));
        final FileObject strjIncludesReplicateDir = paths.replicateDir().resolveFile("strj-includes");
        strjIncludesReplicateDir.delete(new AllFileSelector());
        final ArrayList<ResourcePath> strjIncludeDirs = new ArrayList<>();
        for (FileObject strIncludePath : strIncludePaths) {
            if (!strIncludePath.exists()) {
                continue;
            }
            if (strIncludePath.isFolder()) {
                strjIncludeDirs.add(new FSPath(resourceService.localFile(strIncludePath, strjIncludesReplicateDir)));
            }
        }
        final Arguments extraArgs = new Arguments();
        extraArgs.addAll(config.strArgs());
        extraArgs.add("-la", "java-front");
        if (strExternalJarFlags != null) {
            extraArgs.addLine(strExternalJarFlags);
        }
        final File projectLocation = resourceService.localPath(paths.root());
        assert projectLocation != null;
        final ResourcePath projectPath = new FSPath(projectLocation);
        final ArrayList<Supplier<Stratego2LibInfo>> str2libraries = new ArrayList<>();
        for (LanguageIdentifier sourceDep : config.sourceDeps()) {
            @Nullable
            final ILanguageImpl sourceDepImpl = languageServiceProvider.get().getImpl(sourceDep);
            if (sourceDepImpl == null) {
                continue;
            }
            for (ILanguageComponent sourceDepImplComp : sourceDepImpl.components()) {
                final String[] str2libProject = { null };
                for (IExportConfig export : sourceDepImplComp.config().exports()) {
                    if (str2libProject[0] != null) {
                        break;
                    }
                    export.accept(new IExportVisitor() {

                        @Override
                        public void visit(LangDirExport resource) {
                        }

                        @Override
                        public void visit(LangFileExport resource) {
                            if (resource.language.equals("StrategoLang") && resource.file.endsWith("str2lib")) {
                                str2libProject[0] = resource.file;
                            }
                        }

                        @Override
                        public void visit(ResourceExport resource) {
                        }
                    });
                }
                if (str2libProject[0] != null) {
                    final ResourcePath str2LibFile = new FSPath(resourceService.localFile(sourceDepImplComp.location().resolveFile(str2libProject[0]), paths.replicateDir().resolveFile("strj-includes")));
                    @Nullable
                    final DynamicClassLoadingFacet facet = sourceDepImplComp.facet(DynamicClassLoadingFacet.class);
                    if (facet == null) {
                        continue;
                    }
                    final ArrayList<ResourcePath> jarFiles = new ArrayList<>(facet.jarFiles.size());
                    for (FileObject file : facet.jarFiles) {
                        jarFiles.add(new FSPath(resourceService.localFile(file, paths.replicateDir().resolveFile("str2-includes"))));
                    }
                    str2libraries.add(new ValueSupplier<>(new Stratego2LibInfo(str2LibFile, jarFiles)));
                }
            }
        }
        final ArrayList<STask<?>> sdfTasks = new ArrayList<>(0);
        // Gather all Stratego files to be checked for changes
        final Set<Path> changedFiles = GenerateSourcesBuilder.getChangedFiles(projectLocation);
        final Set<ResourceKey> changedResources = new HashSet<>(changedFiles.size() * 2);
        for (Path changedFile : changedFiles) {
            changedResources.add(new FSPath(changedFile));
        }
        final ArrayList<IModuleImportService.ModuleIdentifier> linkedLibraries = new ArrayList<>();
        GenerateSourcesBuilder.splitOffLinkedLibrariesIncludeDirs(extraArgs, linkedLibraries, strjIncludeDirs, projectLocation.getPath());
        final LastModified<IStrategoTerm> astWLM = new LastModified<>(ast, Instant.now().getEpochSecond());
        final boolean isLibrary = false;
        final ModuleIdentifier moduleIdentifier = new ModuleIdentifier(strFile.getName().endsWith(".str"), isLibrary, moduleName, new FSPath(strFile));
        final ModuleIdentifier mainModuleIdentifier = new ModuleIdentifier(strMainFile.getName().endsWith(".str"), isLibrary, strMainModule, new FSPath(strMainFile));
        final boolean autoImportStd = false;
        final IModuleImportService.ImportResolutionInfo importResolutionInfo = new IModuleImportService.ImportResolutionInfo(sdfTasks, strjIncludeDirs, linkedLibraries, str2libraries);
        final CheckModuleInput checkModuleInput = new CheckModuleInput(new FrontInput.FileOpenInEditor(moduleIdentifier, importResolutionInfo, astWLM, autoImportStd), mainModuleIdentifier, projectPath);
        final Task<CheckOpenModuleOutput> checkModuleTask = checkModuleProvider.get().createTask(checkModuleInput);
        final IPieProvider pieProvider = pieProviderProvider.get();
        final Pie pie = pieProvider.pie();
        final IStrategoList.Builder errors = B.listBuilder();
        final IStrategoList.Builder warnings = B.listBuilder();
        final IStrategoList.Builder notes = B.listBuilder();
        final CheckOpenModuleOutput analysisInformation;
        // noinspection SynchronizationOnLocalVariableOrMethodParameter
        synchronized (pie) {
            GenerateSourcesBuilder.initCompiler(pieProvider, checkModuleTask);
            try (final MixedSession session = pie.newSession()) {
                TopDownSession tdSession = session.updateAffectedBy(changedResources);
                session.deleteUnobservedTasks(t -> true, (t, r) -> {
                    if (Objects.equals(r.getLeafExtension(), "java")) {
                        logger.debug("Deleting garbage from previous build: " + r);
                        return true;
                    }
                    return false;
                });
                analysisInformation = tdSession.getOutput(checkModuleTask);
            } catch (ExecException e) {
                logger.warn("Incremental Stratego build failed", e);
                return null;
            } catch (InterruptedException e) {
                // Ignore
                return null;
            }
        }
        for (Message message : analysisInformation.messages) {
            final IStrategoString term = factory.makeString(message.locationTermString);
            if (message.filename == null) {
                logger.debug("No origins for message: " + message);
            } else {
                // otherwise filename would have also been null
                assert message.sourceRegion != null;
                final ImploderAttachment imploderAttachment = ImploderAttachment.createCompactPositionAttachment(message.filename, message.sourceRegion.startRow, message.sourceRegion.startColumn, message.sourceRegion.startOffset, message.sourceRegion.endOffset);
                term.putAttachment(imploderAttachment);
            }
            final IStrategoTuple messageTuple = B.tuple(term, B.string(message.getMessage()));

        }
        return B.tuple(analysisInformation.astWithCasts, B.list(errors), B.list(warnings), B.list(notes));
    }

    @Nullable
    private ISpoofaxLanguageSpec getLanguageSpecification(IProject project) throws MetaborgException {
        if (languageSpecServiceProvider == null) {
            // Indicates that meta-Spoofax is not available (ISpoofaxLanguageSpecService cannot be injected), but this
            // should never happen because this primitive is inside meta-Spoofax. Check for null just in case.
            logger.error("Language specification service is not available; static injection failed");
            return null;
        }
        final ISpoofaxLanguageSpecService languageSpecService = languageSpecServiceProvider.get();
        if (!languageSpecService.available(project)) {
            logger.error("Language specification service is not available for " + project);
            return null;
        }
        final ISpoofaxLanguageSpec languageSpec;
        try {
            languageSpec = languageSpecService.get(project);
        } catch (ConfigException e) {
            throw new MetaborgException("Unable to get language specification configuration for " + project, e);
        }
        return languageSpec;
    }

    @Override
    public void close() throws Exception {
        pieProviderProvider = null;
        languageSpecServiceProvider = null;
        languageServiceProvider = null;
    }
}


switch(attributeOptions.size()) {
            case 0:
                options = Collections.emptySet();
                break;
            case 1:
                options = Collections.singleton(attributeOptions.iterator().next());
                break;
            default:
                options = new LinkedHashSet<String>(attributeOptions);
                break;
        }

package org.opends.server.api;

import static org.opends.messages.CoreMessages.*;
import static org.opends.server.util.StaticUtils.toLowerCase;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.opends.messages.Message;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.AttributeValues;
import org.opends.server.types.Attributes;
import org.opends.server.types.ByteSequenceReader;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringBuilder;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.ObjectClass;

/**
 * This class provides a utility for interacting with compressed representations
 * of schema elements. The default implementation does not persist encoded
 * attributes and object classes.
 */
@org.opends.server.types.PublicAPI(stability = org.opends.server.types.StabilityLevel.UNCOMMITTED, mayInstantiate = false, mayExtend = true, mayInvoke = false)
public class CompressedSchema {

    // Maps attribute description to ID.
    private final List<Entry<AttributeType, Set<String>>> adDecodeMap;

    // Maps ID to attribute description.
    private final Map<Entry<AttributeType, Set<String>>, Integer> adEncodeMap;

    // The map between encoded representations and object class sets.
    private final List<Map<ObjectClass, String>> ocDecodeMap;

    // The map between object class sets and encoded representations.
    private final Map<Map<ObjectClass, String>, Integer> ocEncodeMap;

    /**
     * Creates a new empty instance of this compressed schema.
     */
    public CompressedSchema() {
        adDecodeMap = new CopyOnWriteArrayList<Entry<AttributeType, Set<String>>>();
        ocDecodeMap = new CopyOnWriteArrayList<Map<ObjectClass, String>>();
        adEncodeMap = new ConcurrentHashMap<Entry<AttributeType, Set<String>>, Integer>();
        ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass, String>, Integer>();
    }

    /**
     * Decodes the contents of the provided array as an attribute at the current
     * position.
     *
     * @param reader
     *          The byte string reader containing the encoded entry.
     * @return The decoded attribute.
     * @throws DirectoryException
     *           If the attribute could not be decoded properly for some reason.
     */
    public final Attribute decodeAttribute(final ByteSequenceReader reader) throws DirectoryException {
        // First decode the encoded attribute description id.
        final int length = reader.getBERLength();
        final byte[] idBytes = new byte[length];
        reader.get(idBytes);
        final int id = decodeId(idBytes);
        // Look up the attribute description.
        Entry<AttributeType, Set<String>> ad = adDecodeMap.get(id);
        if (ad == null) {
            final Message message = ERR_COMPRESSEDSCHEMA_UNRECOGNIZED_AD_TOKEN.get(String.valueOf(id));
            throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
        }
        // Before returning the attribute, make sure that the attribute type is not
        AttributeType attrType = ad.getKey();
        Set<String> options = ad.getValue();
        if (attrType.isDirty()) {
            ad = loadAttribute(idBytes, attrType.getNameOrOID(), options);
            attrType = ad.getKey();
            options = ad.getValue();
        }
        // Determine the number of values for the attribute.
        final int numValues = reader.getBERLength();
        // For the common case of a single value with no options, generate
        // less garbage.
        if (numValues == 1 && options.isEmpty()) {
            final int valueLength = reader.getBERLength();
            final ByteString valueBytes = reader.getByteSequence(valueLength).toByteString();
            return Attributes.create(attrType, AttributeValues.create(attrType, valueBytes));
        } else {
            // Read the appropriate number of values.
            final AttributeBuilder builder = new AttributeBuilder(attrType);
            builder.setOptions(options);
            builder.setInitialCapacity(numValues);
            for (int i = 0; i < numValues; i++) {
                final int valueLength = reader.getBERLength();
                final ByteString valueBytes = reader.getByteSequence(valueLength).toByteString();
                builder.add(AttributeValues.create(attrType, valueBytes));
            }
            return builder.toAttribute();
        }
    }

    /**
     * Decodes an object class set from the provided byte string.
     *
     * @param reader
     *          The byte string reader containing the object class set identifier.
     * @return The decoded object class set.
     * @throws DirectoryException
     *           If the provided byte string reader cannot be decoded as an object
     *           class set.
     */
    public final Map<ObjectClass, String> decodeObjectClasses(final ByteSequenceReader reader) throws DirectoryException {
        // First decode the encoded object class id.
        final int length = reader.getBERLength();
        final byte[] idBytes = new byte[length];
        reader.get(idBytes);
        final int id = decodeId(idBytes);
        // Look up the object classes.
        final Map<ObjectClass, String> ocMap = ocDecodeMap.get(id);
        if (ocMap != null) {
            // Before returning the object classes, make sure that none of them are
            for (final ObjectClass oc : ocMap.keySet()) {
                if (oc.isDirty()) {
                    // Found at least one object class which is dirty so refresh them.
                    return loadObjectClasses(idBytes, ocMap.values());
                }
            }
            return ocMap;
        } else {
            final Message message = ERR_COMPRESSEDSCHEMA_UNKNOWN_OC_TOKEN.get(String.valueOf(id));
            throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
        }
    }

    /**
     * Encodes the information in the provided attribute to a byte array.
     *
     * @param builder
     *          The buffer to encode the attribute to.
     * @param attribute
     *          The attribute to be encoded.
     * @throws DirectoryException
     *           If a problem occurs while attempting to determine the appropriate
     *           identifier.
     */
    public final void encodeAttribute(final ByteStringBuilder builder, final Attribute attribute) throws DirectoryException {
        // Re-use or allocate a new ID.
        final AttributeType type = attribute.getAttributeType();
        final Set<String> options = attribute.getOptions();
        final Entry<AttributeType, Set<String>> ad = new SimpleImmutableEntry<AttributeType, Set<String>>(type, options);
        // Use double checked locking to avoid lazy registration races.
        Integer id = adEncodeMap.get(ad);
        if (id == null) {
            synchronized (adEncodeMap) {
                id = adEncodeMap.get(ad);
                if (id == null) {
                    id = adDecodeMap.size();
                    adDecodeMap.add(ad);
                    adEncodeMap.put(ad, id);
                    storeAttribute(encodeId(id), type.getNameOrOID(), options);
                }
            }
        }
        // Encode the attribute.
        final byte[] idBytes = encodeId(id);
        builder.appendBERLength(idBytes.length);
        builder.append(idBytes);
        builder.appendBERLength(attribute.size());
        for (final AttributeValue v : attribute) {
            builder.appendBERLength(v.getValue().length());
            builder.append(v.getValue());
        }
    }

    /**
     * Encodes the provided set of object classes to a byte array. If the same set
     * had been previously encoded, then the cached value will be used. Otherwise,
     * a new value will be created.
     *
     * @param builder
     *          The buffer to encode the object classes to.
     * @param objectClasses
     *          The set of object classes for which to retrieve the corresponding
     *          byte array token.
     * @throws DirectoryException
     *           If a problem occurs while attempting to determine the appropriate
     *           identifier.
     */
    public final void encodeObjectClasses(final ByteStringBuilder builder, final Map<ObjectClass, String> objectClasses) throws DirectoryException {
        // Re-use or allocate a new ID.
        // Use double checked locking to avoid lazy registration races.
        Integer id = ocEncodeMap.get(objectClasses);
        if (id == null) {
            synchronized (ocEncodeMap) {
                id = ocEncodeMap.get(objectClasses);
                if (id == null) {
                    id = ocDecodeMap.size();
                    ocDecodeMap.add(objectClasses);
                    ocEncodeMap.put(objectClasses, id);
                    storeObjectClasses(encodeId(id), objectClasses.values());
                }
            }
        }
        // Encode the object classes.
        final byte[] idBytes = encodeId(id);
        builder.appendBERLength(idBytes.length);
        builder.append(idBytes);
    }

    /**
     * Returns a view of the encoded attributes in this compressed schema which
     * can be used for saving the entire content to disk. The iterator returned by
     * this method is not thread safe.
     *
     * @return A view of the encoded attributes in this compressed schema.
     */
    protected final Iterable<Entry<byte[], Entry<String, Collection<String>>>> getAllAttributes() {
        return new Iterable<Entry<byte[], Entry<String, Collection<String>>>>() {

            @Override
            public Iterator<Entry<byte[], Entry<String, Collection<String>>>> iterator() {
                return new Iterator<Entry<byte[], Entry<String, Collection<String>>>>() {

                    private int id = 0;

                    @Override
                    public boolean hasNext() {
                        return id < adDecodeMap.size();
                    }

                    @Override
                    public Entry<byte[], Entry<String, Collection<String>>> next() {
                        final byte[] encodedAttribute = encodeId(id);
                        final Entry<AttributeType, Set<String>> ad = adDecodeMap.get(id++);
                        return new SimpleImmutableEntry<byte[], Entry<String, Collection<String>>>(encodedAttribute, new SimpleImmutableEntry<String, Collection<String>>(ad.getKey().getNameOrOID(), ad.getValue()));
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    /**
     * Returns a view of the encoded object classes in this compressed schema
     * which can be used for saving the entire content to disk. The iterator
     * returned by this method is not thread safe.
     *
     * @return A view of the encoded object classes in this compressed schema.
     */
    protected final Iterable<Entry<byte[], Collection<String>>> getAllObjectClasses() {
        return new Iterable<Entry<byte[], Collection<String>>>() {

            @Override
            public Iterator<Entry<byte[], Collection<String>>> iterator() {
                return new Iterator<Map.Entry<byte[], Collection<String>>>() {

                    private int id = 0;

                    @Override
                    public boolean hasNext() {
                        return id < ocDecodeMap.size();
                    }

                    @Override
                    public Entry<byte[], Collection<String>> next() {
                        final byte[] encodedObjectClasses = encodeId(id);
                        final Map<ObjectClass, String> ocMap = ocDecodeMap.get(id++);
                        return new SimpleImmutableEntry<byte[], Collection<String>>(encodedObjectClasses, ocMap.values());
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    /**
     * Loads an encoded attribute into this compressed schema. This method may
     * called by implementations during initialization when loading content from
     * disk.
     *
     * @param encodedAttribute
     *          The encoded attribute description.
     * @param attributeName
     *          The user provided attribute type name.
     * @param attributeOptions
     *          The non-null but possibly empty set of attribute options.
     * @return The attribute type description.
     */
    protected final Entry<AttributeType, Set<String>> loadAttribute(final byte[] encodedAttribute, final String attributeName, final Collection<String> attributeOptions) {
        final AttributeType type = DirectoryServer.getAttributeType(toLowerCase(attributeName), true);
        final Set<String> options;

        final Entry<AttributeType, Set<String>> ad = new SimpleImmutableEntry<AttributeType, Set<String>>(type, options);
        final int id = decodeId(encodedAttribute);
        synchronized (adEncodeMap) {
            adEncodeMap.put(ad, id);
            if (id < adDecodeMap.size()) {
                adDecodeMap.set(id, ad);
            } else {
                // Grow the decode array.
                while (id > adDecodeMap.size()) {
                    adDecodeMap.add(null);
                }
                adDecodeMap.add(ad);
            }
        }
        return ad;
    }

    /**
     * Loads an encoded object class into this compressed schema. This method may
     * called by implementations during initialization when loading content from
     * disk.
     *
     * @param encodedObjectClasses
     *          The encoded object classes.
     * @param objectClassNames
     *          The user provided set of object class names.
     * @return The object class set.
     */
    protected final Map<ObjectClass, String> loadObjectClasses(final byte[] encodedObjectClasses, final Collection<String> objectClassNames) {
        final LinkedHashMap<ObjectClass, String> ocMap = new LinkedHashMap<ObjectClass, String>(objectClassNames.size());
        for (final String name : objectClassNames) {
            final String lowerName = toLowerCase(name);
            final ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
            ocMap.put(oc, name);
        }
        final int id = decodeId(encodedObjectClasses);
        synchronized (ocEncodeMap) {
            ocEncodeMap.put(ocMap, id);
            if (id < ocDecodeMap.size()) {
                ocDecodeMap.set(id, ocMap);
            } else {
                // Grow the decode array.
                while (id > ocDecodeMap.size()) {
                    ocDecodeMap.add(null);
                }
                ocDecodeMap.add(ocMap);
            }
        }
        return ocMap;
    }

    /**
     * Persists the provided encoded attribute. The default implementation is to
     * do nothing. Calls to this method are synchronized, so implementations can
     * assume that this method is not being called by other threads. Note that
     * this method is not thread-safe with respect to
     * {@link #storeObjectClasses(byte[], Collection)}.
     *
     * @param encodedAttribute
     *          The encoded attribute description.
     * @param attributeName
     *          The user provided attribute type name.
     * @param attributeOptions
     *          The non-null but possibly empty set of attribute options.
     * @throws DirectoryException
     *           If an error occurred while persisting the encoded attribute.
     */
    protected void storeAttribute(final byte[] encodedAttribute, final String attributeName, final Collection<String> attributeOptions) throws DirectoryException {
        // Do nothing by default.
    }

    /**
     * Persists the provided encoded object classes. The default implementation is
     * to do nothing. Calls to this method are synchronized, so implementations
     * can assume that this method is not being called by other threads. Note that
     * this method is not thread-safe with respect to
     * {@link #storeAttribute(byte[], String, Collection)}.
     *
     * @param encodedObjectClasses
     *          The encoded object classes.
     * @param objectClassNames
     *          The user provided set of object class names.
     * @throws DirectoryException
     *           If an error occurred while persisting the encoded object classes.
     */
    protected void storeObjectClasses(final byte[] encodedObjectClasses, final Collection<String> objectClassNames) throws DirectoryException {
        // Do nothing by default.
    }

    /**
     * Decodes the provided encoded schema element ID.
     *
     * @param idBytes
     *          The encoded schema element ID.
     * @return The schema element ID.
     */
    private int decodeId(final byte[] idBytes) {
        int id = 0;
        for (final byte b : idBytes) {
            id <<= 8;
            id |= (b & 0xFF);
        }
        // Subtract 1 to compensate for old behavior.
        return id - 1;
    }

    /**
     * Encodes the provided schema element ID.
     *
     * @param id
     *          The schema element ID.
     * @return The encoded schema element ID.
     */
    private byte[] encodeId(final int id) {
        // Add 1 to compensate for old behavior.
        final int value = id + 1;
        final byte[] idBytes;
        if (value <= 0xFF) {
            idBytes = new byte[1];
            idBytes[0] = (byte) (value & 0xFF);
        } else if (value <= 0xFFFF) {
            idBytes = new byte[2];
            idBytes[0] = (byte) ((value >> 8) & 0xFF);
            idBytes[1] = (byte) (value & 0xFF);
        } else if (value <= 0xFFFFFF) {
            idBytes = new byte[3];
            idBytes[0] = (byte) ((value >> 16) & 0xFF);
            idBytes[1] = (byte) ((value >> 8) & 0xFF);
            idBytes[2] = (byte) (value & 0xFF);
        } else {
            idBytes = new byte[4];
            idBytes[0] = (byte) ((value >> 24) & 0xFF);
            idBytes[1] = (byte) ((value >> 16) & 0xFF);
            idBytes[2] = (byte) ((value >> 8) & 0xFF);
            idBytes[3] = (byte) (value & 0xFF);
        }
        return idBytes;
    }
}


switch(this.code) {
            case 300:
            case 301:
            case 302:
            case 303:
            case StatusLine.HTTP_TEMP_REDIRECT:
            case StatusLine.HTTP_PERM_REDIRECT:
                return true;
            default:
                return false;
        }

package com.android.okhttp;

import com.android.okhttp.internal.http.OkHeaders;
import com.android.okhttp.internal.http.StatusLine;
import java.util.Collections;
import java.util.List;

public final class Response {

    private final ResponseBody body;

    private volatile CacheControl cacheControl;

    private Response cacheResponse;

    private final int code;

    private final Handshake handshake;

    private final Headers headers;

    private final String message;

    private Response networkResponse;

    private final Response priorResponse;

    private final Protocol protocol;

    private final Request request;

    public static class Builder {

        private ResponseBody body;

        private Response cacheResponse;

        private int code;

        private Handshake handshake;

        private com.android.okhttp.Headers.Builder headers;

        private String message;

        private Response networkResponse;

        private Response priorResponse;

        private Protocol protocol;

        private Request request;

        public Builder() {
            this.code = -1;
            this.headers = new com.android.okhttp.Headers.Builder();
        }

        private Builder(Response response) {
            this.code = -1;
            this.request = response.request;
            this.protocol = response.protocol;
            this.code = response.code;
            this.message = response.message;
            this.handshake = response.handshake;
            this.headers = response.headers.newBuilder();
            this.body = response.body;
            this.networkResponse = response.networkResponse;
            this.cacheResponse = response.cacheResponse;
            this.priorResponse = response.priorResponse;
        }

        public Builder request(Request request) {
            this.request = request;
            return this;
        }

        public Builder protocol(Protocol protocol) {
            this.protocol = protocol;
            return this;
        }

        public Builder code(int code) {
            this.code = code;
            return this;
        }

        public Builder message(String message) {
            this.message = message;
            return this;
        }

        public Builder handshake(Handshake handshake) {
            this.handshake = handshake;
            return this;
        }

        public Builder header(String name, String value) {
            this.headers.set(name, value);
            return this;
        }

        public Builder addHeader(String name, String value) {
            this.headers.add(name, value);
            return this;
        }

        public Builder removeHeader(String name) {
            this.headers.removeAll(name);
            return this;
        }

        public Builder headers(Headers headers) {
            this.headers = headers.newBuilder();
            return this;
        }

        public Builder body(ResponseBody body) {
            this.body = body;
            return this;
        }

        public Builder networkResponse(Response networkResponse) {
            if (networkResponse != null) {
                checkSupportResponse("networkResponse", networkResponse);
            }
            this.networkResponse = networkResponse;
            return this;
        }

        public Builder cacheResponse(Response cacheResponse) {
            if (cacheResponse != null) {
                checkSupportResponse("cacheResponse", cacheResponse);
            }
            this.cacheResponse = cacheResponse;
            return this;
        }

        private void checkSupportResponse(String name, Response response) {
            if (response.body != null) {
                throw new IllegalArgumentException(name + ".body != null");
            } else if (response.networkResponse != null) {
                throw new IllegalArgumentException(name + ".networkResponse != null");
            } else if (response.cacheResponse != null) {
                throw new IllegalArgumentException(name + ".cacheResponse != null");
            } else if (response.priorResponse != null) {
                throw new IllegalArgumentException(name + ".priorResponse != null");
            }
        }

        public Builder priorResponse(Response priorResponse) {
            if (priorResponse != null) {
                checkPriorResponse(priorResponse);
            }
            this.priorResponse = priorResponse;
            return this;
        }

        private void checkPriorResponse(Response response) {
            if (response.body != null) {
                throw new IllegalArgumentException("priorResponse.body != null");
            }
        }

        public Response build() {
            if (this.request == null) {
                throw new IllegalStateException("request == null");
            } else if (this.protocol == null) {
                throw new IllegalStateException("protocol == null");
            } else if (this.code >= 0) {
                return new Response();
            } else {
                throw new IllegalStateException("code < 0: " + this.code);
            }
        }
    }

    private Response(Builder builder) {
        this.request = builder.request;
        this.protocol = builder.protocol;
        this.code = builder.code;
        this.message = builder.message;
        this.handshake = builder.handshake;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.networkResponse = builder.networkResponse;
        this.cacheResponse = builder.cacheResponse;
        this.priorResponse = builder.priorResponse;
    }

    public Request request() {
        return this.request;
    }

    public Protocol protocol() {
        return this.protocol;
    }

    public int code() {
        return this.code;
    }

    public boolean isSuccessful() {
        return this.code >= 200 && this.code < 300;
    }

    public String message() {
        return this.message;
    }

    public Handshake handshake() {
        return this.handshake;
    }

    public List<String> headers(String name) {
        return this.headers.values(name);
    }

    public String header(String name) {
        return header(name, null);
    }

    public String header(String name, String defaultValue) {
        String result = this.headers.get(name);
        return result != null ? result : defaultValue;
    }

    public Headers headers() {
        return this.headers;
    }

    public ResponseBody body() {
        return this.body;
    }

    public Builder newBuilder() {
        return new Builder();
    }

    public boolean isRedirect() {

    }

    public Response networkResponse() {
        return this.networkResponse;
    }

    public Response cacheResponse() {
        return this.cacheResponse;
    }

    public Response priorResponse() {
        return this.priorResponse;
    }

    public List<Challenge> challenges() {
        String responseField;
        if (this.code == 401) {
            responseField = "WWW-Authenticate";
        } else if (this.code != 407) {
            return Collections.emptyList();
        } else {
            responseField = "Proxy-Authenticate";
        }
        return OkHeaders.parseChallenges(headers(), responseField);
    }

    public CacheControl cacheControl() {
        CacheControl result = this.cacheControl;
        if (result != null) {
            return result;
        }
        result = CacheControl.parse(this.headers);
        this.cacheControl = result;
        return result;
    }

    public String toString() {
        return "Response{protocol=" + this.protocol + ", code=" + this.code + ", message=" + this.message + ", url=" + this.request.urlString() + '}';
    }
}


switch(view.getId()) {
            case R.id.btn_task_noresult:
                noResult();
                break;
            case R.id.btn_task_withresult:
                withResult();
                break;
        }

package com.androidwind.androidquick.demo.features.module.task;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.androidwind.androidquick.demo.R;
import com.androidwind.androidquick.demo.base.BaseFragment;
import com.androidwind.task.SimpleTask;
import com.androidwind.task.Task;
import com.androidwind.task.TinyTaskExecutor;
import butterknife.OnClick;

/**
 * @author ddnosh
 * @website http://example.com
 * <p>
 * about tinytask: ---> http://example.com
 */
public class TaskTinyFragment extends BaseFragment {

    @Override
    protected int getContentViewLayoutID() {
        return R.layout.fragment_task_tiny;
    }

    @Override
    protected void initViewsAndEvents(Bundle savedInstanceState) {
    }

    @OnClick({ R.id.btn_task_noresult, R.id.btn_task_withresult })
    public void onClick(View view) {

    }

    private void withResult() {
        TinyTaskExecutor.execute(new Task<String>() {

            @Override
            public String doInBackground() {
                System.out.println("[new] thread id in tinytask: " + Thread.currentThread().getId());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("[new] with callback after 5 sec");
                return "task with sleep 5 sec";
            }

            @Override
            public void onSuccess(String s) {
                Toast.makeText(getActivity(), s, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFail(Throwable throwable) {
            }
        });
    }

    private void noResult() {
        TinyTaskExecutor.execute(new SimpleTask() {

            @Override
            public void run() {
                System.out.println("[new] thread id in tinytask: " + Thread.currentThread().getId());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("[new] no callback after 5 sec");
            }
        });
    }
}


switch(str.charAt(0)) {
            case 't':
            case '1':
                return Boolean.TRUE;
        }

package com.thaiopensource.datatype.xsd;

import org.relaxng.datatype.ValidationContext;

class BooleanDatatype extends DatatypeBase {

    boolean lexicallyAllows(String str) {
        return str.equals("true") || str.equals("false") || str.equals("1") || str.equals("0");
    }

    Object getValue(String str, ValidationContext vc) {

        return Boolean.FALSE;
    }

    String getLexicalSpaceKey() {
        return "boolean";
    }
}