Java tutorial
/******************************************************************************* * Copyright 2009, 2010 Lars Grammel, Bo Fu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package org.thechiselgroup.biomixer.client.core.visualization; import java.util.ArrayList; import java.util.List; import org.thechiselgroup.biomixer.client.core.error_handling.ThrowableCaught; import org.thechiselgroup.biomixer.client.core.persistence.Memento; import org.thechiselgroup.biomixer.client.core.persistence.Persistable; import org.thechiselgroup.biomixer.client.core.persistence.PersistableRestorationService; import org.thechiselgroup.biomixer.client.core.resources.ResourceByPropertyMultiCategorizer; import org.thechiselgroup.biomixer.client.core.resources.ResourceByUriMultiCategorizer; import org.thechiselgroup.biomixer.client.core.resources.ResourceMultiCategorizer; import org.thechiselgroup.biomixer.client.core.resources.persistence.ResourceSetAccessor; import org.thechiselgroup.biomixer.client.core.resources.persistence.ResourceSetCollector; import org.thechiselgroup.biomixer.client.core.ui.ImageButton; import org.thechiselgroup.biomixer.client.core.ui.SidePanelSection; import org.thechiselgroup.biomixer.client.core.ui.widget.listbox.ListBoxControl; import org.thechiselgroup.biomixer.client.core.ui.widget.listbox.VisibilityChangeEvent; import org.thechiselgroup.biomixer.client.core.ui.widget.listbox.VisibilityChangeHandler; import org.thechiselgroup.biomixer.client.core.util.DisposeUtil; import org.thechiselgroup.biomixer.client.core.util.NoSuchAdapterException; import org.thechiselgroup.biomixer.client.core.util.collections.LightweightCollection; import org.thechiselgroup.biomixer.client.core.visualization.model.ViewContentDisplay; import org.thechiselgroup.biomixer.client.core.visualization.model.VisualizationModel; import org.thechiselgroup.biomixer.client.core.visualization.model.extensions.ResourceModel; import org.thechiselgroup.biomixer.client.core.visualization.model.extensions.SelectionModel; import org.thechiselgroup.biomixer.client.core.visualization.model.managed.ManagedSlotMappingConfiguration; import org.thechiselgroup.biomixer.client.core.visualization.model.persistence.ManagedSlotMappingConfigurationPersistence; import org.thechiselgroup.biomixer.client.core.visualization.ui.VisualMappingsControl; import org.thechiselgroup.biomixer.shared.core.util.ForTest; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.DockPanel; import com.google.gwt.user.client.ui.HasAlignment; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.StackPanel; import com.google.gwt.user.client.ui.Widget; public class DefaultView implements View { private class ViewPanel extends DockPanel implements ViewProvider { /** * Enables finding this view by searching the widget hierarchy. */ @Override public View get() { return DefaultView.this; } @Override protected void onDetach() { dispose(); super.onDetach(); } /* * Fixes bug that map is not resized if window is resized */ @Override public void setPixelSize(int width, int height) { DefaultView.this.setSize(width, height); super.setPixelSize(width, height); } } private static final String MEMENTO_LABEL_KEY = "label"; private static final String CSS_EXPANDER = "DefaultView-Expander"; private static final String CSS_CONFIGURATION_PANEL = "DefaultView-ConfigurationPanel"; private static final String CSS_VIEW_CONFIGURATION_PANEL = "view-configurationPanel"; private static final String CSS_VIEW_ERROR_LIST_BOX = "view-errorListBox"; private DockPanel topBar; private List<ViewTopBarExtension> topBarExtensions = new ArrayList<ViewTopBarExtension>(); private StackPanel sidePanel; /** * Sections that will be displayed in the side panel. This is a lightweight * collections so we can check whether it is empty or not. */ private final LightweightCollection<SidePanelSection> sidePanelSections; /** * The main panel of this view. It contains all other widgets of this view. */ private ViewPanel viewPanel; private int width; private int height; private final ViewContentDisplay contentDisplay; private VisualizationModel visualizationModel; private final ResourceModel resourceModel; private final SelectionModel selectionModel; private boolean isInitialized; private final String contentType; private final ListBoxControl<ThrowableCaught> errorListBoxControl; private String label; private final ManagedSlotMappingConfigurationPersistence managedSlotMappingConfigurationPersistence; private final ManagedSlotMappingConfiguration managedSlotMappingConfiguration; private final DisposeUtil disposeUtil; private static final String MEMENTO_CONTENT_DISPLAY = "content-display"; private static final String MEMENTO_RESOURCE_MODEL = "resource-model"; private static final String MEMENTO_SELECTION_MODEL = "selection-model"; private static final String MEMENTO_SLOT_MAPPINGS = "slot-mappings"; private static final String MEMENTO_GROUPING = "grouping"; // TODO change parameter order in constructor // TODO change contentDisplay to more restricted interface public DefaultView(ViewContentDisplay contentDisplay, String label, String contentType, VisualMappingsControl visualMappingsControl, LightweightCollection<SidePanelSection> sidePanelSections, VisualizationModel visualizationModel, ResourceModel resourceModel, SelectionModel selectionModel, ManagedSlotMappingConfiguration managedSlotMappingConfiguration, ManagedSlotMappingConfigurationPersistence managedSlotMappingConfigurationPersistence, DisposeUtil disposeUtil, ListBoxControl<ThrowableCaught> errorListBoxControl) { assert label != null; assert contentType != null; assert contentDisplay != null; assert sidePanelSections != null; assert visualizationModel != null; assert resourceModel != null; assert selectionModel != null; assert managedSlotMappingConfiguration != null; assert managedSlotMappingConfigurationPersistence != null; assert disposeUtil != null; assert errorListBoxControl != null; this.label = label; this.contentType = contentType; this.contentDisplay = contentDisplay; this.sidePanelSections = sidePanelSections; this.visualizationModel = visualizationModel; this.selectionModel = selectionModel; this.resourceModel = resourceModel; this.managedSlotMappingConfiguration = managedSlotMappingConfiguration; this.managedSlotMappingConfigurationPersistence = managedSlotMappingConfigurationPersistence; this.disposeUtil = disposeUtil; this.errorListBoxControl = errorListBoxControl; registerErrorListBoxVisibilityChangeHandler(); } @Override public <T> T adaptTo(Class<T> clazz) throws NoSuchAdapterException { return contentDisplay.adaptTo(clazz); } @Override public void addTopBarExtension(ViewTopBarExtension extension) { assert extension != null; assert !isInitialized; this.topBarExtensions.add(extension); } @Override public Widget asWidget() { return viewPanel; } @Override public void dispose() { for (ViewTopBarExtension extension : topBarExtensions) { disposeUtil.safelyDispose(extension); } topBarExtensions = null; visualizationModel = disposeUtil.safelyDispose(visualizationModel); } @ForTest protected void doRestore(Memento state, PersistableRestorationService restorationService, ResourceSetAccessor accessor) { assert isInitialized : "view has to be initialized before restoring it"; contentDisplay.startRestore(); // restoreGrouping(state.getChild(MEMENTO_GROUPING)); restoreGrouping(state.getChild(MEMENTO_GROUPING)); restore(resourceModel, state, MEMENTO_RESOURCE_MODEL, restorationService, accessor); restore(selectionModel, state, MEMENTO_SELECTION_MODEL, restorationService, accessor); contentDisplay.restore(state.getChild(MEMENTO_CONTENT_DISPLAY), restorationService, accessor); managedSlotMappingConfigurationPersistence.restore(managedSlotMappingConfiguration, state.getChild(MEMENTO_SLOT_MAPPINGS)); setLabel((String) state.getValue(MEMENTO_LABEL_KEY)); contentDisplay.endRestore(); } @Override public String getContentType() { return contentType; } @Override public String getLabel() { return label; } @Override public VisualizationModel getModel() { assert visualizationModel != null : "view has been disposed"; return visualizationModel; } protected String getModuleBase() { return GWT.getModuleBaseURL(); } @Override public ResourceModel getResourceModel() { return resourceModel; } @Override public SelectionModel getSelectionModel() { return selectionModel; } @Override public void init() { assert !isInitialized : "view has already been initialized"; initUI(); isInitialized = true; } private void initConfigurationPanelUI() { topBar = new DockPanel(); topBar.setSize("100%", ""); topBar.setStyleName(CSS_VIEW_CONFIGURATION_PANEL); for (ViewTopBarExtension extension : topBarExtensions) { extension.init(topBar); } initSideBarExpander(); } private void initSideBar() { assert sidePanel == null; assert sidePanelSections != null; sidePanel = new StackPanel(); sidePanel.setStyleName(CSS_CONFIGURATION_PANEL); sidePanel.setVisible(false); for (SidePanelSection sidePanelSection : sidePanelSections) { /* * wrap widget to enable scrolling when there is not enough space on * the y-axis. */ /* * SimplePanel wrapperPanel = new SimplePanel( * sidePanelSection.getWidget()); Style style = * wrapperPanel.getElement().getStyle(); style.setHeight(100, * Unit.PCT); style.setOverflowY(Overflow.AUTO); * * sidePanel.add(wrapperPanel, sidePanelSection.getSectionTitle()); */ ScrollPanel scrollPanel = new ScrollPanel(sidePanelSection.getWidget()); scrollPanel.setAlwaysShowScrollBars(false); Style style = scrollPanel.getElement().getStyle(); com.google.gwt.user.client.Window.enableScrolling(true); style.setHeight(100, Unit.PCT); style.setOverflow(Overflow.AUTO); sidePanel.add(scrollPanel, sidePanelSection.getSectionTitle()); } } private void initSideBarExpander() { ImageButton expander = ImageButton.createExpanderButton(); expander.setStyleName(CSS_EXPANDER); expander.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { sidePanel.setVisible(!sidePanel.isVisible()); updateContentDisplaySize(); } }); topBar.add(expander, DockPanel.EAST); topBar.setCellHorizontalAlignment(expander, HasAlignment.ALIGN_RIGHT); } protected void initUI() { initConfigurationPanelUI(); initSideBar(); viewPanel = new ViewPanel(); viewPanel.setBorderWidth(0); viewPanel.setSpacing(0); viewPanel.setSize("500px", "300px"); viewPanel.add(topBar, DockPanel.NORTH); viewPanel.add(contentDisplay.asWidget(), DockPanel.CENTER); viewPanel.add(sidePanel, DockPanel.EAST); /* * removing error list box Widget errorListBox = * errorListBoxControl.asWidget(); errorListBox.setSize("100%", ""); * errorListBox.setStyleName(CSS_VIEW_ERROR_LIST_BOX); * errorListBox.setVisible(false); viewPanel.add(errorListBox, * DockPanel.SOUTH); */ viewPanel.setCellHeight(contentDisplay.asWidget(), "100%"); } @Override public boolean isAdaptableTo(Class<?> clazz) { return contentDisplay.isAdaptableTo(clazz); } // XXX remove once content display lifecycle working @Override public boolean isReady() { return contentDisplay.isReady(); } private void registerErrorListBoxVisibilityChangeHandler() { errorListBoxControl.registerVisibilityChangeHandler(new VisibilityChangeHandler() { @Override public void onVisibilityChange(VisibilityChangeEvent event) { updateContentDisplaySize(); } }); } @Override public void restore(final Memento state, final PersistableRestorationService restorationService, final ResourceSetAccessor accessor) { /* * wait for content to be ready (needed for graph view swf loading on * restore) */ // XXX this might be the cause for issue 25 // TODO introduce proper view content display lifecycle instead if (contentDisplay.isReady()) { doRestore(state, restorationService, accessor); } else { new Timer() { @Override public void run() { restore(state, restorationService, accessor); } }.schedule(200); } } private void restore(Object target, Memento parentMemento, String targetMementoKey, PersistableRestorationService restorationService, ResourceSetAccessor accessor) { if (target instanceof Persistable) { ((Persistable) target).restore(parentMemento.getChild(targetMementoKey), restorationService, accessor); } } // TODO extract constants private void restoreGrouping(Memento groupingMemento) { assert groupingMemento != null; String categorizerType = (String) groupingMemento.getValue("type"); if ("byProperty".equals(categorizerType)) { String property = (String) groupingMemento.getValue("property"); visualizationModel.setCategorizer(new ResourceByPropertyMultiCategorizer(property)); } else if ("byUri".equals(categorizerType)) { visualizationModel.setCategorizer(new ResourceByUriMultiCategorizer()); } } private void save(Object target, Memento parentMemento, String targetMementoKey, ResourceSetCollector resourceSetCollector) { if (target instanceof Persistable) { parentMemento.addChild(targetMementoKey, ((Persistable) target).save(resourceSetCollector)); } } @Override public Memento save(ResourceSetCollector resourceSetCollector) { Memento memento = new Memento(); saveGrouping(memento); save(selectionModel, memento, MEMENTO_SELECTION_MODEL, resourceSetCollector); save(resourceModel, memento, MEMENTO_RESOURCE_MODEL, resourceSetCollector); memento.addChild(MEMENTO_CONTENT_DISPLAY, contentDisplay.save(resourceSetCollector)); memento.addChild(MEMENTO_SLOT_MAPPINGS, managedSlotMappingConfigurationPersistence.save(managedSlotMappingConfiguration)); memento.setValue(MEMENTO_LABEL_KEY, getLabel()); return memento; } // TODO extract constants private void saveGrouping(Memento memento) { Memento groupingMemento = new Memento(); ResourceMultiCategorizer categorizer = visualizationModel.getCategorizer(); if (categorizer instanceof ResourceByPropertyMultiCategorizer) { groupingMemento.setValue("type", "byProperty"); groupingMemento.setValue("property", ((ResourceByPropertyMultiCategorizer) categorizer).getProperty()); } else if (categorizer instanceof ResourceByUriMultiCategorizer) { groupingMemento.setValue("type", "byUri"); } memento.addChild(MEMENTO_GROUPING, groupingMemento); } @Override public void setLabel(String label) { this.label = label; } /** * Sets the size of the default view, not including decorations such as * border, margin, and padding (and thus different from offset width and * height). * * @param width * width in pixels * @param height * height in pixels */ private void setSize(int width, int height) { assert height >= 0; assert width >= 0; this.width = width; this.height = height; updateContentDisplaySize(); } private void updateContentDisplaySize() { /* * special resize method required, because otherwise window height * cannot be reduced by dragging - see * http://code.google.com/p/google-web-toolkit/issues/detail?id=316 */ int targetHeight = errorListBoxControl.isVisible() ? height - topBar.getOffsetHeight() - errorListBoxControl.asWidget().getOffsetHeight() : height - topBar.getOffsetHeight(); int targetWidth = sidePanel.isVisible() ? width - sidePanel.getOffsetWidth() : width; contentDisplay.setSize(targetWidth, targetHeight); } }