123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.ui;
-
- import static java.nio.charset.StandardCharsets.UTF_8;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
-
- import org.jsoup.nodes.Element;
-
- import com.vaadin.server.JsonPaintTarget;
- import com.vaadin.server.PaintException;
- import com.vaadin.server.PaintTarget;
- import com.vaadin.shared.ui.customlayout.CustomLayoutState;
- import com.vaadin.ui.declarative.DesignContext;
-
- /**
- * <p>
- * A container component with freely designed layout and style. The layout
- * consists of items with textually represented locations. Each item contains
- * one sub-component, which can be any Vaadin component, such as a layout. The
- * adapter and theme are responsible for rendering the layout with a given style
- * by placing the items in the defined locations.
- * </p>
- *
- * <p>
- * The placement of the locations is not fixed - different themes can define the
- * locations in a way that is suitable for them. One typical example would be to
- * create visual design for a web site as a custom layout: the visual design
- * would define locations for "menu", "body", and "title", for example. The
- * layout would then be implemented as an HTML template for each theme.
- * </p>
- *
- * <p>
- * A location is identified with the attribute "data-location" or "location"
- * which has the location name as its value.
- * </p>
- *
- * <p>
- * The default theme handles the styles that are not defined by drawing the
- * subcomponents just as in OrderedLayout.
- * </p>
- *
- * @author Vaadin Ltd.
- * @since 3.0
- */
- @SuppressWarnings("serial")
- public class CustomLayout extends AbstractLayout implements LegacyComponent {
-
- private static final int BUFFER_SIZE = 10000;
-
- /**
- * Custom layout slots containing the components.
- */
- private final Map<String, Component> slots = new HashMap<>();
-
- /**
- * Default constructor only used by subclasses. Subclasses are responsible
- * for setting the appropriate fields. Either
- * {@link #setTemplateName(String)}, that makes layout fetch the template
- * from theme, or {@link #setTemplateContents(String)}.
- *
- * @since 7.5.0
- */
- public CustomLayout() {
- setWidth(100, Unit.PERCENTAGE);
- }
-
- /**
- * Constructs a custom layout with the template given in the stream.
- *
- * @param templateStream
- * Stream containing template data. Must be using UTF-8 encoding.
- * To use a String as a template use for instance new
- * ByteArrayInputStream("<template>".getBytes()).
- * @throws IOException
- */
- public CustomLayout(InputStream templateStream) throws IOException {
- this();
- initTemplateContentsFromInputStream(templateStream);
- }
-
- /**
- * Constructor for custom layout with given template name. Template file is
- * fetched from "<theme>/layouts/<templateName>".
- */
- public CustomLayout(String template) {
- this();
- setTemplateName(template);
- }
-
- protected void initTemplateContentsFromInputStream(
- InputStream templateStream) throws IOException {
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(templateStream, UTF_8));
- StringBuilder builder = new StringBuilder(BUFFER_SIZE);
- try {
- char[] cbuf = new char[BUFFER_SIZE];
- int nRead;
- while ((nRead = reader.read(cbuf, 0, BUFFER_SIZE)) > 0) {
- builder.append(cbuf, 0, nRead);
- }
- } finally {
- reader.close();
- }
-
- setTemplateContents(builder.toString());
- }
-
- @Override
- protected CustomLayoutState getState() {
- return (CustomLayoutState) super.getState();
- }
-
- @Override
- protected CustomLayoutState getState(boolean markAsDirty) {
- return (CustomLayoutState) super.getState(markAsDirty);
- }
-
- /**
- * Adds the component into this container to given location. If the location
- * is already populated, the old component is removed.
- *
- * @param c
- * the component to be added.
- * @param location
- * the location of the component.
- */
- public void addComponent(Component c, String location) {
- final Component old = slots.get(location);
- if (old != null) {
- removeComponent(old);
- }
- slots.put(location, c);
- getState().childLocations.put(c, location);
-
- super.addComponent(c);
- }
-
- /**
- * Adds the component into this container. The component is added without
- * specifying the location (empty string is then used as location). Only one
- * component can be added to the default "" location and adding more
- * components into that location overwrites the old components.
- *
- * @param c
- * the component to be added.
- */
- @Override
- public void addComponent(Component c) {
- this.addComponent(c, "");
- }
-
- /**
- * Removes the component from this container.
- *
- * @param c
- * the component to be removed.
- */
- @Override
- public void removeComponent(Component c) {
- if (c == null) {
- return;
- }
- slots.values().remove(c);
- getState().childLocations.remove(c);
- super.removeComponent(c);
- }
-
- /**
- * Removes the component from this container from given location.
- *
- * @param location
- * the Location identifier of the component.
- */
- public void removeComponent(String location) {
- this.removeComponent(slots.get(location));
- }
-
- /**
- * Gets the component container iterator for going trough all the components
- * in the container.
- *
- * @return the Iterator of the components inside the container.
- */
- @Override
- public Iterator<Component> iterator() {
- return Collections.unmodifiableCollection(slots.values()).iterator();
- }
-
- /**
- * Gets the number of contained components. Consistent with the iterator
- * returned by {@link #getComponentIterator()}.
- *
- * @return the number of contained components
- */
- @Override
- public int getComponentCount() {
- return slots.values().size();
- }
-
- /**
- * Gets the child-component by its location.
- *
- * @param location
- * the name of the location where the requested component
- * resides.
- * @return the Component in the given location or null if not found.
- */
- public Component getComponent(String location) {
- return slots.get(location);
- }
-
- /* Documented in superclass */
- @Override
- public void replaceComponent(Component oldComponent,
- Component newComponent) {
-
- // Gets the locations
- String oldLocation = null;
- String newLocation = null;
- for (final String location : slots.keySet()) {
- final Component component = slots.get(location);
- if (component == oldComponent) {
- oldLocation = location;
- }
- if (component == newComponent) {
- newLocation = location;
- }
- }
-
- if (oldLocation == null) {
- addComponent(newComponent);
- } else if (newLocation == null) {
- removeComponent(oldLocation);
- addComponent(newComponent, oldLocation);
- } else {
- slots.put(newLocation, oldComponent);
- slots.put(oldLocation, newComponent);
- getState().childLocations.put(newComponent, oldLocation);
- getState().childLocations.put(oldComponent, newLocation);
- }
- }
-
- /** Get the name of the template. */
- public String getTemplateName() {
- return getState(false).templateName;
- }
-
- /** Get the contents of the template. */
- public String getTemplateContents() {
- return getState(false).templateContents;
- }
-
- /**
- * Set the name of the template used to draw custom layout.
- *
- * With GWT-adapter, the template with name 'templatename' is loaded from
- * VAADIN/themes/themename/layouts/templatename.html. If the theme has not
- * been set (with Application.setTheme()), themename is 'default'.
- *
- * @param templateName
- */
- public void setTemplateName(String templateName) {
- getState().templateName = templateName;
- getState().templateContents = null;
- }
-
- /**
- * Set the contents of the template used to draw the custom layout.
- *
- * Note: setTemplateContents can be applied only before CustomLayout
- * instance has been attached.
- *
- * @param templateContents
- */
- public void setTemplateContents(String templateContents) {
- getState().templateContents = templateContents;
- getState().templateName = null;
- }
-
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- // Nothing to see here
- }
-
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- // Workaround to make the CommunicationManager read the template file
- // and send it to the client
- String templateName = getState(false).templateName;
- if (templateName != null && !templateName.isEmpty()) {
- Set<Object> usedResources = ((JsonPaintTarget) target)
- .getUsedResources();
- String resourceName = "layouts/" + templateName + ".html";
- usedResources.add(resourceName);
- }
- }
-
- @Override
- public void readDesign(Element design, DesignContext designContext) {
- super.readDesign(design, designContext);
-
- for (Element child : design.children()) {
-
- Component childComponent = designContext.readDesign(child);
-
- if (child.hasAttr(":location")) {
- addComponent(childComponent, child.attr(":location"));
- } else {
- addComponent(childComponent);
- }
- }
- }
-
- @Override
- public void writeDesign(Element design, DesignContext designContext) {
- super.writeDesign(design, designContext);
-
- for (Entry<String, Component> slot : slots.entrySet()) {
- Element child = designContext.createElement(slot.getValue());
- if (slots.size() > 1 || !"".equals(slot.getKey())) {
- child.attr(":location", slot.getKey());
- }
- design.appendChild(child);
- }
- }
- }
|