Skip to content

Content of file DefaultLayout.java

/*
 * #%L
 * *********************************************************************************************************************
 *
 * NorthernWind - lightweight CMS
 * http://northernwind.tidalwave.it - git clone https://bitbucket.org/tidalwave/northernwind-src.git
 * %%
 * Copyright (C) 2011 - 2023 Tidalwave s.a.s. (http://tidalwave.it)
 * %%
 * *********************************************************************************************************************
 *
 * 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.
 *
 * *********************************************************************************************************************
 *
 *
 * *********************************************************************************************************************
 * #L%
 */
package it.tidalwave.northernwind.frontend.impl.ui;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Map;
import org.springframework.beans.factory.annotation.Configurable;
import it.tidalwave.util.As;
import it.tidalwave.util.Id;
import it.tidalwave.util.NotFoundException;
import it.tidalwave.util.spi.AsSupport;
import it.tidalwave.northernwind.core.model.HttpStatusException;
import it.tidalwave.northernwind.core.model.SiteNode;
import it.tidalwave.northernwind.frontend.ui.Layout;
import it.tidalwave.northernwind.frontend.ui.LayoutFinder;
import it.tidalwave.northernwind.frontend.ui.ViewFactory;
import it.tidalwave.northernwind.frontend.ui.ViewFactory.ViewAndController;
import lombok.experimental.Delegate;
import lombok.Getter;

/***********************************************************************************************************************
 *
 * @author  Fabrizio Giudici
 *
 **********************************************************************************************************************/
@Configurable @Getter
public class DefaultLayout implements Layout, Cloneable
  {
    @Inject
    private ViewFactory viewFactory;

    @Nonnull
    private final Id id;

    @Nonnull
    private /* final FIXME */ String typeUri;

    private final List<Layout> children = new ArrayList<>();

    private final Map<Id, Layout> childrenMapById = new HashMap<>();

    @Delegate
    private final As asSupport = new AsSupport(this);

    // FIXME: make it Immutable

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    static class CloneVisitor extends VisitorSupport<Layout, DefaultLayout>
      {
        private DefaultLayout rootLayout;

        private final Deque<DefaultLayout> layoutStack = new ArrayDeque<>();

        @Override
        public void preVisit (@Nonnull final Layout layout)
          {
            final DefaultLayout clone = new DefaultLayout(((DefaultLayout)layout).id,
                                                          ((DefaultLayout)layout).typeUri);

            if (rootLayout == null)
              {
                rootLayout = clone;
              }
            else
              {
                layoutStack.peek().add(clone);
              }

            layoutStack.push(clone);
          }

        @Override
        public void postVisit (@Nonnull final Layout layout)
          {
            layoutStack.pop();
          }

        @Override @Nonnull
        public DefaultLayout getValue()
          {
            return rootLayout;
          }
      }

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    public DefaultLayout()
      {
        this.id = new Id("");
        this.typeUri = "";
      }

    /** Clone - FIXME: public only for InfoglueImporter */
    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    public DefaultLayout (@Nonnull final Id id, @Nonnull final String typeUri)
      {
        this.id = id;
        this.typeUri = typeUri;
      }

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    public DefaultLayout (@Nonnull final Builder builder)
      {
        this.id = builder.getId();
        this.typeUri = builder.getType();
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override @Nonnull
    public DefaultLayout clone()
      {
        return accept(new CloneVisitor()).orElseThrow(RuntimeException::new);
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override @Nonnull
    public Layout withOverride (@Nonnull final Layout override)
      {
        final DefaultLayout result = clone();
        result.applyOverride(((DefaultLayout)override).clone());
        return result;
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override @Nonnull
    public Layout withChild (@Nonnull final Layout layout)
      {
        final DefaultLayout clone = clone();
        clone.children.add(layout);
        clone.childrenMapById.put(layout.getId(), layout);

        return clone;
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override @Nonnull
    public LayoutFinder findChildren()
      {
        return new DefaultLayoutFinder(children, childrenMapById);
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void add (@Nonnull final Layout layout) // FIXME: drop this - used only by the CloneVisitor and Infoglue converter
      {
        children.add(layout); // FIXME: clone
        childrenMapById.put(layout.getId(), layout);// FIXME: clone
clone
} /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public ViewAndController createViewAndController (@Nonnull final SiteNode siteNode) throws NotFoundException, HttpStatusException { return viewFactory.createViewAndController(typeUri, id, siteNode); } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull // TODO: push up to CompositeSupport public <T> Optional<T> accept (@Nonnull final Visitor<Layout, T> visitor) { visitor.preVisit(this); visitor.visit(this); for (final Layout child : children) { child.accept(visitor); } visitor.postVisit(this); try { return Optional.ofNullable(visitor.getValue()); // TODO: visitor.getOptionalValue() } catch (NotFoundException e) { return Optional.empty(); } } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public String toString() { return String.format("DefaultLayout(id=%s, typeUri=%s, children.count=%d)", id, typeUri, children.size()); } /******************************************************************************************************************* * * * ******************************************************************************************************************/ // Here everything is already cloned private void applyOverride (@Nonnull final Layout override) { final boolean sameType = this.getTypeUri().equals(override.getTypeUri()); this.typeUri = override.getTypeUri(); // FIXME: don't like this approach, as it requires typeUri non final // Complex rule, but it's to keep compatibility with Infoglue. if (sameType) { for (final Layout overridingChild : override.findChildren().results()) { final Layout overriddenChild = childrenMapById.get(overridingChild.getId()); if (overriddenChild == null) { add(overridingChild); } else { childrenMapById.put(overridingChild.getId(), overridingChild); final int i = children.indexOf(overriddenChild); if (i < 0) { throw new IllegalArgumentException(); } children.set(i, overridingChild); // ((DefaultLayout)overriddenChild).applyOverride(overridingChild); } } } else { this.children.clear(); this.childrenMapById.clear(); for (final Layout overridingChild : override.findChildren().results()) { add(overridingChild); } } } }