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 extends ComponentModel> componentModel;
- private final String type;
public ComponentModelTypeLiteral(
- final Class extends ComponentModel> componentModel,
- final String type) {
+ final Class extends ComponentModel> 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 extends ComponentModel> 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 extends CcmApplication> 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 extends CcmApplication> applicationType;
-
- public PageModelTypeLiteral(
- final String type,
- final Class extends CcmApplication> applicationType) {
-
- this.type = type;
- this.applicationType = applicationType;
- }
-
- @Override
- public String type() {
- return type;
- }
-
- @Override
- public Class extends CcmApplication> 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 extends CcmApplication> 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;
+ }
+
+ }
+
+}