From cdc2904a3ba2cf0df3602f7485ac4eab26de77f8 Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 11 Oct 2017 10:44:47 +0000 Subject: [PATCH] CCM NG: Cleanup of PageModel, first part of theming git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5037 8810af33-2d31-482b-a856-94f89814c4df --- .../pagemodel/AbstractPageBuilder.java | 42 ++--- .../libreccm/pagemodel/ComponentBuilder.java | 7 +- .../pagemodel/ComponentBuilderManager.java | 44 ++--- .../pagemodel/ComponentModelType.java | 1 - .../org/libreccm/pagemodel/PageBuilder.java | 9 +- .../pagemodel/PageBuilderManager.java | 124 -------------- .../bebop/AbstractBebopPageBuilder.java | 70 -------- .../org/libreccm/theming/ThemeFileInfo.java | 144 ++++++++++++++++ .../java/org/libreccm/theming/ThemeInfo.java | 124 ++++++++++++++ .../ThemeProcessor.java} | 27 +-- .../org/libreccm/theming/ThemeProvider.java | 155 ++++++++++++++++++ .../java/org/libreccm/theming/ThemeType.java | 29 ++++ .../org/libreccm/theming/ThemeVersion.java | 30 ++++ .../java/org/libreccm/theming/Themes.java | 135 +++++++++++++++ 14 files changed, 664 insertions(+), 277 deletions(-) delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilderManager.java delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/bebop/AbstractBebopPageBuilder.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java rename ccm-core/src/main/java/org/libreccm/{pagemodel/PageModelType.java => theming/ThemeProcessor.java} (58%) create mode 100644 ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/ThemeType.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/ThemeVersion.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/Themes.java diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageBuilder.java b/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageBuilder.java index e65b0bd18..1f5866335 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageBuilder.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageBuilder.java @@ -18,7 +18,10 @@ */ package org.libreccm.pagemodel; +import java.util.Map; + import javax.inject.Inject; + import java.util.Optional; /** @@ -32,7 +35,7 @@ import java.util.Optional; * @author Jens Pelzetter * */ -public abstract class AbstractPageBuilder

implements PageBuilder

{ +public abstract class AbstractPageBuilder implements PageBuilder { @Inject private ComponentBuilderManager componentBuilderManager; @@ -49,14 +52,16 @@ public abstract class AbstractPageBuilder

implements PageBuilder

{ * @return A page containing all components from the {@link PageModel}. */ @Override - public P buildPage(final PageModel pageModel) { - final P page = buildPage(); + public Map buildPage(final PageModel pageModel) { + + final Map page = buildPage(); for (final ComponentModel componentModel : pageModel.getComponents()) { final Optional component = buildComponent( componentModel, componentModel.getClass()); if (component.isPresent()) { - addComponent(page, component); + page.put(componentModel.getIdAttribute(), + component); } } @@ -77,35 +82,18 @@ public abstract class AbstractPageBuilder

implements PageBuilder

{ final ComponentModel componentModel, final Class componentModelClass) { - componentBuilderManager.findComponentBuilder(componentModel.getClass(), - getType()); + componentBuilderManager.findComponentBuilder(componentModel.getClass()); - final Optional> builder = componentBuilderManager - .findComponentBuilder(componentModelClass, getType()); + final Optional> builder = componentBuilderManager + .findComponentBuilder(componentModelClass); if (builder.isPresent()) { - return Optional.of(builder.get().buildComponent((M) componentModel)); + @SuppressWarnings("unchecked") + final M model = (M) componentModel; + return Optional.of(builder.get().buildComponent(model)); } else { return Optional.empty(); } } - /** - * Abstract method returning the type (view technology) for which the - * {@link PageBuilder} processes {@link PageModel}s. - * - * @return - */ - protected abstract String getType(); - - /** - * A helper method for adding components to the page. How this is done - * depends on the view technology, therefore this method must be implemented - * by the implementations of this abstract class. - * - * @param page The page to which the component is added. - * @param component The component to add to the page. - */ - protected abstract void addComponent(P page, Object component); - } diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilder.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilder.java index 419004aa6..254ae52e1 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilder.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilder.java @@ -18,6 +18,8 @@ */ package org.libreccm.pagemodel; +import java.util.Map; + /** * A {@code ComponentBuilder} transforms a {@link ComponentModel} into a * component. @@ -25,10 +27,9 @@ package org.libreccm.pagemodel; * * @author Jens Pelzetter * @param Type of the model the component builder processes. - * @param Type of the component which is build from the model. */ -public interface ComponentBuilder { +public interface ComponentBuilder { - C buildComponent(M componentModel); + Map buildComponent(M componentModel); } diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilderManager.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilderManager.java index 3b5052209..be09285e3 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilderManager.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentBuilderManager.java @@ -41,11 +41,11 @@ public class ComponentBuilderManager { ComponentBuilderManager.class); @Inject - private Instance> componentBuilders; + private Instance> componentBuilders; /** * Find an implementation of the {@link ComponentBuilder} interface for a - * specific {@link ComponentModel} and type. + * specific {@link ComponentModel}. * * @param Generic variable for the subtype of * {@link ComponentModel} which is produced by @@ -53,29 +53,24 @@ public class ComponentBuilderManager { * @param componentModelClass The sub class of the {@link ComponentModel} * for which is processed by the * {@link ComponentBuilder}. - * @param type The type for which the - * {@link ComponentBuilder} produces the - * component(s). * * @return An {@link Optional} containing the implementation of the * {@link ComponentBuilder} interface for the specified parameters. - * If there is no implementation of the specified parameters an + * If there is no implementation for the specified parameters an * empty {@link Optional} is returned. */ @SuppressWarnings("unchecked") - public Optional> findComponentBuilder( - final Class componentModelClass, - final String type) { + public Optional> findComponentBuilder( + final Class componentModelClass) { LOGGER.debug("Trying to find ComponentBuilder for ComponentModel\"{}\"" + "and type \"{}\"...", - componentModelClass.getName(), - type); + componentModelClass.getName()); final ComponentModelTypeLiteral literal = new ComponentModelTypeLiteral( - componentModelClass, type); + componentModelClass); - final Instance> instance = componentBuilders + final Instance> instance = componentBuilders .select(literal); if (instance.isUnsatisfied()) { LOGGER.warn("No ComponentBuilder for component model \"%s\" " @@ -83,16 +78,15 @@ public class ComponentBuilderManager { return Optional.empty(); } else if (instance.isAmbiguous()) { throw new IllegalStateException(String.format( - "Multiple ComponentBuilders for component model \"%s\" and " - + "type \"%s\" available. Something is wrong", - componentModelClass.getName(), - type)); + "Multiple ComponentBuilders for component model \"%s\"available. " + + "Something is wrong", + componentModelClass.getName())); } else { - final Iterator> iterator = instance. + final Iterator> iterator = instance. iterator(); - final ComponentBuilder componentBuilder = iterator.next(); + final ComponentBuilder componentBuilder = iterator.next(); - return Optional.of((ComponentBuilder) componentBuilder); + return Optional.of((ComponentBuilder) componentBuilder); } } @@ -104,13 +98,10 @@ public class ComponentBuilderManager { private static final long serialVersionUID = -2601632434295178600L; private final Class componentModel; - private final String type; public ComponentModelTypeLiteral( - final Class componentModel, - final String type) { + final Class componentModel) { this.componentModel = componentModel; - this.type = type; } @Override @@ -118,11 +109,6 @@ public class ComponentBuilderManager { return componentModel; } - @Override - public String type() { - return type; - } - } } diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelType.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelType.java index 9c07da24d..80f33864d 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelType.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelType.java @@ -37,6 +37,5 @@ public @interface ComponentModelType { Class componentModel(); - String type(); } diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilder.java b/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilder.java index 7359f4074..0d4374540 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilder.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilder.java @@ -18,6 +18,8 @@ */ package org.libreccm.pagemodel; +import java.util.Map; + import javax.enterprise.context.RequestScoped; /** @@ -32,9 +34,8 @@ import javax.enterprise.context.RequestScoped; * * * @author Jens Pelzetter - * @param

The type of page the page builder creates. */ -public interface PageBuilder

{ +public interface PageBuilder { /** * Build a page for the view technology supported by this page builder @@ -44,7 +45,7 @@ public interface PageBuilder

{ * * @return A page with the default components. */ - P buildPage(); + Map buildPage(); /** * Build a page of type {@code P} using the provided {@link PageModel}. @@ -55,7 +56,7 @@ public interface PageBuilder

{ * * @return The page generated from the provided {@link PageModel}. */ - P buildPage(PageModel pageModel); + Map buildPage(PageModel pageModel); diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilderManager.java b/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilderManager.java deleted file mode 100644 index a12f05612..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/PageBuilderManager.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2016 LibreCCM Foundation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -package org.libreccm.pagemodel; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.libreccm.web.CcmApplication; - -import javax.enterprise.context.RequestScoped; -import javax.enterprise.inject.Instance; -import javax.enterprise.util.AnnotationLiteral; -import javax.inject.Inject; -import java.util.Iterator; -import java.util.Optional; - -/** - * Provides access to all available {@link PageBuilder} implementations. - * - * @author Jens Pelzetter - */ -@RequestScoped -public class PageBuilderManager { - - private static final Logger LOGGER = LogManager.getLogger( - PageBuilderManager.class); - - @Inject - private Instance> pageBuilders; - - /** - * Find a {@link PageBuilder} for a specific type and application type. - * - * @param type The type of the {@link PageBuilder}. - * @param applicationType The application type for which the - * {@link PageBuilder} builds pages. - * - * @return An {@link Optional} containing the {@link PageBuilder} - * implementation for the specified {@code type} and - * {@code applicationType}. If there is no {@code PageBuilder} for - * the specified parameters an empty {@link Optional} is returned. - */ - public Optional> findPageBuilder( - final String type, - final Class applicationType) { - - LOGGER.debug("Trying to find PageBuilder for type \"{}\" and " - + "application type \"{}\"...", - type, - applicationType); - - final PageModelTypeLiteral literal = new PageModelTypeLiteral( - type, applicationType); - - final Instance> instance = pageBuilders.select(literal); - if (instance.isUnsatisfied()) { - LOGGER.warn("No PageBuilder for type \"{}\" and application type " - + "\"{}\" available.", - type, - applicationType); - return Optional.empty(); - } else if (instance.isAmbiguous()) { - throw new IllegalArgumentException(String.format( - "Multiple PageBuilders for type \"%s\" and " - + "application type \"%s\" avilable. Something is wrong.", - type, - applicationType)); - } else { - LOGGER.debug("Found PageBuilder for type \"{}\" and application " - + "type \"{}\"...", - type, - applicationType); - final Iterator> iterator = instance.iterator(); - final PageBuilder pageBuilder = iterator.next(); - - return Optional.of(pageBuilder); - } - } - - private class PageModelTypeLiteral - extends AnnotationLiteral - implements PageModelType { - - private static final long serialVersionUID = 5919950993273871601L; - - private final String type; - private final Class applicationType; - - public PageModelTypeLiteral( - final String type, - final Class applicationType) { - - this.type = type; - this.applicationType = applicationType; - } - - @Override - public String type() { - return type; - } - - @Override - public Class applicationType() { - return applicationType; - } - - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/bebop/AbstractBebopPageBuilder.java b/ccm-core/src/main/java/org/libreccm/pagemodel/bebop/AbstractBebopPageBuilder.java deleted file mode 100644 index 6aa7faa80..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/bebop/AbstractBebopPageBuilder.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2016 LibreCCM Foundation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -package org.libreccm.pagemodel.bebop; - -import com.arsdigita.bebop.Component; -import com.arsdigita.bebop.Page; -import com.arsdigita.bebop.PageFactory; -import com.arsdigita.web.Web; -import org.libreccm.pagemodel.AbstractPageBuilder; - -/** - * Basic implementation of a {@link PageBuilder} for Bebop {@link Page}s. - * Applications must provided an implementation of the {@link PageBuilder}. - * These implementations must override the - * {@link #addDefaultComponents(com.arsdigita.bebop.Page)} method. - * - * @author Jens Pelzetter - */ -public abstract class AbstractBebopPageBuilder extends AbstractPageBuilder { - - public static final String BEBOP = "Bebop"; - - @Override - protected String getType() { - return BEBOP; - } - - @Override - protected void addComponent(final Page page, final Object component) { - final Component bebopComponent = (Component) component; - - page.add(bebopComponent); - } - - @Override - public Page buildPage() { - - final String application = Web.getWebContext().getApplication(). - getPrimaryUrl(); - final Page page = PageFactory.buildPage(application, ""); - - addDefaultComponents(page); - - return page; - } - - /** - * Add the default components which are present on every page. - * - * @param page The page to which the components are added. - */ - public abstract void addDefaultComponents(final Page page); - -} diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java new file mode 100644 index 000000000..0f69aedac --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming; + +import java.util.Objects; + +/** + * Informations about a file in a theme. + * + * @author Jens Pelzetter + */ +public class ThemeFileInfo { + + private String name; + private boolean directory; + private String mimeType; + private long size; + private boolean writable; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public boolean isDirectory() { + return directory; + } + + public void setDirectory(final boolean directory) { + this.directory = directory; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(final String mimeType) { + this.mimeType = mimeType; + } + + public long getSize() { + return size; + } + + public void setSize(final long size) { + this.size = size; + } + + public boolean isWritable() { + return writable; + } + + public void setWritable(final boolean writable) { + this.writable = writable; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 83 * hash + Objects.hashCode(name); + hash = 83 * hash + (directory ? 1 : 0); + hash = 83 * hash + Objects.hashCode(mimeType); + hash = 83 * hash + (int) (size ^ (size >>> 32)); + hash = 83 * hash + (writable ? 1 : 0); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ThemeFileInfo)) { + return false; + } + final ThemeFileInfo other = (ThemeFileInfo) obj; + if (!other.canEqual(this)) { + return false; + } + + if (directory != other.isDirectory()) { + return false; + } + if (size != other.getSize()) { + return false; + } + if (writable != other.isWritable()) { + return false; + } + if (!Objects.equals(name, other.getName())) { + return false; + } + return Objects.equals(mimeType, other.getMimeType()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof ThemeFileInfo; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "name = \"%s\", " + + "directory = %b, " + + "mimeType = \"%s\", " + + "size = %d, " + + "writable = %b%s" + + " }", + super.toString(), + name, + directory, + mimeType, + size, + writable, + data); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java new file mode 100644 index 000000000..d1d99f8e2 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming; + +import java.util.Objects; + +/** + * + * @author Jens Pelzetter + */ +public class ThemeInfo { + + private String name; + + private ThemeVersion version; + + private String type; + + private Class provider; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public ThemeVersion getVersion() { + return version; + } + + public void setVersion(final ThemeVersion version) { + this.version = version; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public Class getProvider() { + return provider; + } + + public void setProvider(final Class provider) { + this.provider = provider; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 73 * hash + Objects.hashCode(name); + hash = 73 * hash + Objects.hashCode(version); + hash = 73 * hash + Objects.hashCode(type); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ThemeInfo)) { + return false; + } + final ThemeInfo other = (ThemeInfo) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(name, other.getName())) { + return false; + } + if (!Objects.equals(type, other.getType())) { + return false; + } + return version == other.getVersion(); + } + + public boolean canEqual(final Object obj) { + return obj instanceof ThemeInfo; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "name = \"%s\", " + + "version = %s, " + + "type = \"%s\"%s" + + " }", + super.toString(), + name, + Objects.toString(version), + type, + data); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/PageModelType.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java similarity index 58% rename from ccm-core/src/main/java/org/libreccm/pagemodel/PageModelType.java rename to ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java index a9adf5f5a..79f9f5685 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/PageModelType.java +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 LibreCCM Foundation. + * Copyright (C) 2017 LibreCCM Foundation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,29 +16,18 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ -package org.libreccm.pagemodel; +package org.libreccm.theming; -import org.libreccm.web.CcmApplication; - -import javax.inject.Qualifier; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.util.Map; /** - * Specifies for which application type and which view technology a - * {@link PageBuilder} builds the pages. * * @author Jens Pelzetter */ -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface PageModelType { - - String type(); - - Class applicationType(); +public interface ThemeProcessor { + + String process(Map page, + ThemeInfo theme, + ThemeProvider themeProvider); } diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java new file mode 100644 index 000000000..fcfc214c7 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Optional; + +/** + * A theme provider provides access to the files of a theme. It abstracts from + * the location and method of loading. + * + * @author Jens Pelzetter + */ +public interface ThemeProvider { + + /** + * Provides a list of all themes provided by this theme provider. The list + * should be ordered by the name of the theme. + * + * @return A list of all themes provided by this theme provider. If the + * implementation supports draft and live themes the list should + * contain all draft themes. + */ + List getThemes(); + + /** + * Provides a list of all live themes provided by this theme provider. + * + * @return A list of all live themes provided by this theme provider. If the + * implementation does not support draft/live themes the + * implementation of this method returns the same list as + * {@link #getThemes()}. + */ + List getLiveThemes(); + + /** + * Provide information about a theme. + * + * @param theme The theme. + * @param version The version of the theme. Implementations which do not + * support draft/live themes will ignore this parameter. + * + * @return Informations about the theme identified by the provided name. If + * there is no such theme provided by this {@code ThemeProvider} an + * empty optional is returned. + */ + Optional getThemeInfo(String theme, ThemeVersion version); + + /** + * List all files in a theme at the specified path. + * + * @param theme The theme of which the files are listed. + * @param version The version of the theme for which the files are listed. + * Implementations which do not support draft/live themes + * will ignore this parameter. + * @param path The path of the directory of which the files are listed. + * The path is relative to the root of the theme.To get the + * root directory provided an empty string. Implementations + * should throw an NullPointerException if {@code null} is + * provided as path. + * + * @return A list of all files in the provided directory. If there is such + * path in the theme the list is empty. If the path is the path of a + * file and not a directory the list should have one element, the + * data about the file itself. + */ + List listThemeFiles(String theme, + ThemeVersion version, + String path); + + /** + * Retrieve a file from a theme. We use an {@link InputStream} here because + * that is the most universal interface in the Java API which works for all + * sorts of resources and is independent from any other API. + * + * @param theme The theme from which the file is retrieved. + * @param version The version of the theme from which the file is retrieved. + * Implementations which do not support draft/live themes + * will ignore this parameter. + * @param path The path of file to retrieve relative to the root of the + * theme. + * + * @return An {@link Optional} containing an {@link InputStream} for the + * requested file or an empty optional if the theme has no such + * file. + */ + Optional getThemeFileAsStream(String theme, + ThemeVersion version, + String path); + + /** + * Creates an {@link OutputStream} for a theme file. Implementations which + * do not support changes to the theme files should throw an + * {@link UnsupportedOperationException}. If the file is not writable for + * some reason an {@link IllegalArgumentException} should be thrown. + * + * If an implementation supports draft/live themes the {@link OutputStream} + * always changes the file in the draft version of the theme. The live theme + * should only be changed by {@link #publishTheme(String). + * + * If the file does not exist it the file is created. + * + * If not all directories in the provided path already exist an + * implementation should create the missing directories. + * + * @param theme The theme to which the file belongs. + * @param path The path of the file to update + * + * @return An {@link OutputStream} for the file. + */ + OutputStream getOutputStreamForThemeFile(String theme, String path); + + /** + * Determines if the implementation supports changes to the files of the + * themes. + * + * @return + */ + boolean supportsChanges(); + + /** + * Determines if the implementation supports draft/live themes. + * + * @return + */ + boolean supportsDraftThemes(); + + /** + * Publishes all changes done to a draft theme to its live version. For + * implementations which do not support draft/live themes the implementation + * of this method should be a noop, but not throw an exception. + * + * @param theme The theme to publish. + */ + void publishTheme(String theme); + +} diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeType.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeType.java new file mode 100644 index 000000000..374211ef6 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeType.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming; + +/** + * + * @author Jens Pelzetter + */ +public @interface ThemeType { + + String value(); + +} diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeVersion.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeVersion.java new file mode 100644 index 000000000..f6e4a0dee --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeVersion.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming; + +/** + * + * @author Jens Pelzetter + */ +public enum ThemeVersion { + + DRAFT, + LIVE + +} diff --git a/ccm-core/src/main/java/org/libreccm/theming/Themes.java b/ccm-core/src/main/java/org/libreccm/theming/Themes.java new file mode 100644 index 000000000..03487db75 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/Themes.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.core.UnexpectedErrorException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Instance; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Inject; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class Themes { + + private static final Logger LOGGER = LogManager.getLogger(Themes.class); + + @Inject + private Instance providers; + + @Inject + private Instance processors; + + public List getAvailableThemes() { + + final List themes = new ArrayList<>(); + for (final ThemeProvider provider : providers) { + themes.addAll(provider.getThemes()); + } + + return themes; + } + + public List getLiveThemes() { + + final List themes = new ArrayList<>(); + for (final ThemeProvider provider : providers) { + themes.addAll(provider.getLiveThemes()); + } + + return themes; + } + + public String process(Map page, ThemeInfo theme) { + + final ThemeTypeLiteral themeType = new ThemeTypeLiteral(theme.getType()); + + final Instance forType = processors.select(themeType); + if (forType.isUnsatisfied()) { + LOGGER.error("No ThemeProcessor implementation for type \"{}\" of " + + "theme \"{}\".", + theme.getType(), + theme.getName()); + throw new UnexpectedErrorException(String + .format("No ThemeProcessor implementation for type \"%s\" of " + + "theme \"%s\".", + theme.getType(), + theme.getName())); + } + + if (forType.isAmbiguous()) { + LOGGER.error( + "Mutiple ThemeProcessor implementations for type \"{}\" of " + + "theme \"{}\".", + theme.getType(), + theme.getName()); + throw new UnexpectedErrorException(String + .format( + "Mutiple ThemeProcessor implementations for type \"%s\" of " + + "theme \"%s\".", + theme.getType(), + theme.getName())); + } + + final Instance forTheme = providers.select(theme + .getProvider()); + + if (forTheme.isUnsatisfied()) { + LOGGER.error("ThemeProvider \"{}\" not found.", + theme.getProvider().getName()); + throw new UnexpectedErrorException(String.format( + "ThemeProvider \"%s\" not found.", + theme.getProvider().getName())); + } + + final ThemeProcessor processor = forType.get(); + final ThemeProvider provider = forTheme.get(); + + return processor.process(page, theme, provider); + } + + private class ThemeTypeLiteral extends AnnotationLiteral + implements ThemeType { + + private static final long serialVersionUID = 3377237291286175824L; + + private final String value; + + public ThemeTypeLiteral(final String value) { + this.value = value; + } + + @Override + public String value() { + return value; + } + + } + +}