CCM NG: Simplified rendering logic for ContentItem component

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5062 8810af33-2d31-482b-a856-94f89814c4df
ccm-docs
jensp 2017-10-19 13:15:20 +00:00
parent a7919d9204
commit ba6e28df85
8 changed files with 77 additions and 354 deletions

View File

@ -21,32 +21,18 @@ package org.librecms.pagemodel;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.LocalizedString;
import org.libreccm.pagemodel.ComponentBuilder; import org.libreccm.pagemodel.ComponentBuilder;
import org.libreccm.security.PermissionChecker; import org.libreccm.security.PermissionChecker;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager; import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges;
import org.librecms.pagemodel.contentitems.AbstractContentItemRenderer;
import org.librecms.pagemodel.contentitems.ContentItemRenderers;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.transaction.Transactional; import javax.transaction.Transactional;
@ -67,6 +53,9 @@ public abstract class AbstractContentItemComponentBuilder<T extends ContentItemC
@Inject @Inject
private ConfigurationManager confManager; private ConfigurationManager confManager;
@Inject
private ContentItemRenderers contentItemRenderers;
@Inject @Inject
private ContentItemL10NManager iteml10nManager; private ContentItemL10NManager iteml10nManager;
@ -146,259 +135,13 @@ public abstract class AbstractContentItemComponentBuilder<T extends ContentItemC
} }
if (iteml10nManager.hasLanguage(item, language)) { if (iteml10nManager.hasLanguage(item, language)) {
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(item.getClass());
} catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex);
}
final PropertyDescriptor[] properties = beanInfo final AbstractContentItemRenderer renderer = contentItemRenderers
.getPropertyDescriptors(); .findRenderer(item.getClass(), componentModel.getMode());
final Map<String, Object> result = new HashMap<>(); return renderer.render(item, language);
for (final PropertyDescriptor propertyDescriptor : properties) {
renderProperty(propertyDescriptor,
componentModel,
language,
item,
result);
}
return result;
} else { } else {
throw new NotFoundException("Requested language is not available."); throw new NotFoundException("Requested language is not available.");
} }
} }
protected void renderProperty(final PropertyDescriptor propertyDescriptor,
final T componentModel,
final Locale language,
final ContentItem item,
final Map<String, Object> result) {
final String propertyName = propertyDescriptor.getName();
if (componentModel.getExcludedPropertyPaths().contains(propertyName)) {
return;
}
final Method readMethod = propertyDescriptor.getReadMethod();
if (Collection.class.isAssignableFrom(propertyDescriptor
.getPropertyType())) {
final Collection<?> collection;
try {
collection = (Collection<?>) readMethod.invoke(item);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
final List<Map<String, Object>> associatedObjs = new ArrayList<>();
for (final Object obj : collection) {
associatedObjs.add(generateAssociatedObject(obj, language));
}
result.put(propertyName, associatedObjs);
} else if (isValueType(propertyDescriptor.getPropertyType())) {
try {
result.put(propertyName, readMethod.invoke(item));
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
} else if (LocalizedString.class.isAssignableFrom(propertyDescriptor
.getPropertyType())) {
final LocalizedString localizedString;
try {
localizedString = (LocalizedString) readMethod.invoke(item);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
result.put(propertyName, localizedString.getValue(language));
} else {
final Map<String, Object> associatedObj;
try {
associatedObj = generateAssociatedObject(
readMethod.invoke(item), language);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
result.put(propertyName, associatedObj);
}
final Set<String> includedPropertyPaths = componentModel
.getIncludedPropertyPaths();
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(item.getClass());
} catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex);
}
final Map<String, PropertyDescriptor> propertyDescriptors = Arrays
.stream(beanInfo.getPropertyDescriptors())
.collect(Collectors.toMap(PropertyDescriptor::getName,
descriptor -> descriptor));
for (final String propertyPath : includedPropertyPaths) {
final String[] pathTokens = propertyPath.split(".");
if (pathTokens.length < 3) {
continue;
}
if (!propertyDescriptors.containsKey(pathTokens[0])) {
continue;
}
final Object propResult = renderPropertyPath(
propertyDescriptors.get(pathTokens[0]),
Arrays.copyOfRange(pathTokens, 1, pathTokens.length),
language,
item);
if (propResult != null) {
result.put(propertyPath, propResult);
}
}
}
protected Object renderPropertyPath(
final PropertyDescriptor propertyDescriptor,
final String[] propertyPath,
final Locale language,
final Object item) {
if (propertyPath.length == 1) {
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(item.getClass());
} catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex);
}
final Map<String, PropertyDescriptor> propertyDescriptors = Arrays
.stream(beanInfo.getPropertyDescriptors())
.collect(Collectors.toMap(PropertyDescriptor::getName,
descriptor -> descriptor));
if (propertyDescriptors.containsKey(propertyPath[0])) {
final Method readMethod = propertyDescriptors
.get(propertyPath[0])
.getReadMethod();
try {
return readMethod.invoke(item);
} catch(IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
} else {
return null;
}
} else {
final Method readMethod = propertyDescriptor.getReadMethod();
final Object obj;
try {
obj = readMethod.invoke(item);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
if (isValueType(obj.getClass())) {
return null;
}
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(obj.getClass());
} catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex);
}
final Map<String, PropertyDescriptor> propertyDescriptors = Arrays
.stream(beanInfo.getPropertyDescriptors())
.collect(Collectors.toMap(PropertyDescriptor::getName,
descriptor -> descriptor));
if (propertyDescriptors.containsKey(propertyPath[0])) {
return renderPropertyPath(
propertyDescriptors.get(propertyPath[0]),
Arrays.copyOfRange(propertyPath, 1, propertyPath.length),
language,
item);
} else {
return null;
}
}
}
protected Map<String, Object> generateAssociatedObject(
final Object obj, final Locale language) {
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(obj.getClass());
} catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex);
}
final PropertyDescriptor[] properties = beanInfo
.getPropertyDescriptors();
final Map<String, Object> result = new HashMap<>();
for (final PropertyDescriptor propertyDescriptor : properties) {
final Class<?> type = propertyDescriptor.getPropertyType();
if (isValueType(type)) {
final Method readMethod = propertyDescriptor.getReadMethod();
try {
result.put(propertyDescriptor.getName(),
readMethod.invoke(obj));
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
} else if (LocalizedString.class.isAssignableFrom(type)) {
final Method readMethod = propertyDescriptor.getReadMethod();
try {
final LocalizedString str = (LocalizedString) readMethod
.invoke(obj);
result.put(propertyDescriptor.getName(),
str.getValue(language));
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
}
}
return result;
}
protected boolean isValueType(final Class<?> typeToTest) {
final Class<?>[] types = new Class<?>[]{
Boolean.class,
Boolean.TYPE,
Character.class,
Character.TYPE,
Byte.class,
Byte.TYPE,
Double.class,
Double.TYPE,
Float.class,
Float.TYPE,
Integer.class,
Integer.TYPE,
Long.class,
Long.TYPE,
Short.class,
Short.TYPE,
String.class
};
for (final Class<?> type : types) {
if (type.isAssignableFrom(typeToTest)) {
return true;
}
}
return typeToTest.isArray()
&& (typeToTest.getComponentType()
.isAssignableFrom(Byte.class)
|| typeToTest.getComponentType()
.isAssignableFrom(Byte.TYPE));
}
} }

View File

@ -27,6 +27,7 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
@ -46,66 +47,21 @@ public class ContentItemComponent extends ComponentModel {
private static final long serialVersionUID = 4904530823926147281L; private static final long serialVersionUID = 4904530823926147281L;
@ElementCollection @Column(name = "MODE")
@CollectionTable(name = "GREETING_ITEM_INCLUDED_PATHS", private String mode;
schema = DB_SCHEMA,
joinColumns = {
@JoinColumn(name = "GREETING_ITEM_ID")
})
private Set<String> includedPropertyPaths;
@ElementCollection public String getMode() {
@CollectionTable(name = "GREETING_ITEM_EXCLUDED_PATHS", return mode;
schema = DB_SCHEMA,
joinColumns = {
@JoinColumn(name = "GREETING_ITEM_ID")
})
private Set<String> excludedProperties;
public ContentItemComponent() {
includedPropertyPaths = new HashSet<>();
excludedProperties = new HashSet<>();
} }
public Set<String> getIncludedPropertyPaths() { public void setMode(final String mode) {
return Collections.unmodifiableSet(includedPropertyPaths); this.mode = mode;
}
protected void setIncludedPropertyPaths(
final Set<String> includePropertyPaths) {
this.includedPropertyPaths = new HashSet<>(includePropertyPaths);
}
public void addIncludedPropertyPath(final String path) {
includedPropertyPaths.add(path);
}
public void removeIncludedPropertyPath(final String path) {
includedPropertyPaths.remove(path);
}
public Set<String> getExcludedPropertyPaths() {
return Collections.unmodifiableSet(excludedProperties);
}
protected void setExcludedPropertyPaths(
final Set<String> excludedProperties) {
this.excludedProperties = new HashSet<>(excludedProperties);
}
public void addExcludedProperty(final String path) {
excludedProperties.add(path);
}
public void removeExcludedProperty(final String path) {
excludedProperties.remove(path);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int hash = super.hashCode(); int hash = super.hashCode();
hash = 13 * hash + Objects.hashCode(includedPropertyPaths); hash = 13 * hash + Objects.hashCode(mode);
hash = 13 * hash + Objects.hashCode(excludedProperties);
return hash; return hash;
} }
@ -128,12 +84,7 @@ public class ContentItemComponent extends ComponentModel {
return false; return false;
} }
if (!Objects.equals(includedPropertyPaths, return Objects.equals(mode, other.getMode());
other.getIncludedPropertyPaths())) {
return false;
}
return Objects.equals(excludedProperties,
other.getExcludedPropertyPaths());
} }
@Override @Override
@ -144,10 +95,8 @@ public class ContentItemComponent extends ComponentModel {
@Override @Override
public String toString(final String data) { public String toString(final String data) {
return super.toString(String return super.toString(String
.format(", includedPropertyPaths = %s, " .format(", mode = \"%s\"",
+ "excludedProperties = %s%s", Objects.toString(mode),
Objects.toString(includedPropertyPaths),
Objects.toString(excludedProperties),
data)); data));
} }

View File

@ -28,11 +28,11 @@ import java.util.Map;
/** /**
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
* @param <T>
*/ */
public abstract class AbstractContentItemRenderer<T extends ContentItem> { public abstract class AbstractContentItemRenderer {
public Map<String, Object> render(final T item, final Locale language) { public Map<String, Object> render(final ContentItem item,
final Locale language) {
final Map<String, Object> result = new HashMap<>(); final Map<String, Object> result = new HashMap<>();
@ -55,7 +55,7 @@ public abstract class AbstractContentItemRenderer<T extends ContentItem> {
return result; return result;
} }
public abstract void renderItem(final T item, protected abstract void renderItem(final ContentItem item,
final Locale language, final Locale language,
final Map<String, Object> result); final Map<String, Object> result);

View File

@ -18,6 +18,7 @@
*/ */
package org.librecms.pagemodel.contentitems; package org.librecms.pagemodel.contentitems;
import org.librecms.contentsection.ContentItem;
import org.librecms.contenttypes.Article; import org.librecms.contenttypes.Article;
import java.util.Locale; import java.util.Locale;
@ -31,13 +32,20 @@ import javax.enterprise.context.RequestScoped;
*/ */
@ContentItemRenderer(renders = Article.class) @ContentItemRenderer(renders = Article.class)
@RequestScoped @RequestScoped
public class ArticleRenderer extends AbstractContentItemRenderer<Article> { public class ArticleRenderer extends AbstractContentItemRenderer {
@Override @Override
public void renderItem(final Article article, public void renderItem(final ContentItem item,
final Locale language, final Locale language,
final Map<String, Object> result) { final Map<String, Object> result) {
final Article article;
if (item instanceof Article) {
article = (Article) item;
} else {
return;
}
result.put("text", article.getText().getValue(language)); result.put("text", article.getText().getValue(language));
} }

View File

@ -41,18 +41,18 @@ public class ContentItemRenderers {
.getLogger(ContentItemRenderers.class); .getLogger(ContentItemRenderers.class);
@Inject @Inject
private Instance<AbstractContentItemRenderer<?>> renderers; private Instance<AbstractContentItemRenderer> renderers;
public <T extends ContentItem> AbstractContentItemRenderer<T> findRenderer( public AbstractContentItemRenderer findRenderer(
final Class<T> itemType) { final Class<? extends ContentItem> itemType) {
LOGGER.debug("Trying to find default renderer for item type \"{}\"...", LOGGER.debug("Trying to find default renderer for item type \"{}\"...",
itemType.getName()); itemType.getName());
return findRenderer(itemType, "--DEFAULT--"); return findRenderer(itemType, "--DEFAULT--");
} }
public <T extends ContentItem> AbstractContentItemRenderer<T> findRenderer( public AbstractContentItemRenderer findRenderer(
final Class<T> itemType, final String mode) { final Class<? extends ContentItem> itemType, final String mode) {
LOGGER.debug("Trying to find default renderer for item type \"{}\"" LOGGER.debug("Trying to find default renderer for item type \"{}\""
+ "and mode \"{}\"...", + "and mode \"{}\"...",
@ -63,7 +63,7 @@ public class ContentItemRenderers {
= new ContentItemRendererLiteral( = new ContentItemRendererLiteral(
itemType, mode); itemType, mode);
final Instance<AbstractContentItemRenderer<?>> instance = renderers final Instance<AbstractContentItemRenderer> instance = renderers
.select(literal); .select(literal);
if (instance.isUnsatisfied()) { if (instance.isUnsatisfied()) {
@ -72,10 +72,10 @@ public class ContentItemRenderers {
LOGGER.warn("No renderer for item type \"{}\" and mode " LOGGER.warn("No renderer for item type \"{}\" and mode "
+ "\"--DEFAULT--\". Returning default renderer.", + "\"--DEFAULT--\". Returning default renderer.",
itemType.getName()); itemType.getName());
return new AbstractContentItemRenderer<T>() { return new AbstractContentItemRenderer() {
@Override @Override
public void renderItem(final T item, public void renderItem(final ContentItem item,
final Locale language, final Locale language,
final Map<String, Object> result) { final Map<String, Object> result) {
//Nothing here. //Nothing here.
@ -91,13 +91,13 @@ public class ContentItemRenderers {
return findRenderer(itemType); return findRenderer(itemType);
} }
} else { } else {
final AbstractContentItemRenderer<?> renderer = instance final AbstractContentItemRenderer renderer = instance
.iterator() .iterator()
.next(); .next();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final AbstractContentItemRenderer<T> result final AbstractContentItemRenderer result
= (AbstractContentItemRenderer<T>) renderer; = renderer;
return result; return result;
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.librecms.pagemodel.contentitems; package org.librecms.pagemodel.contentitems;
import org.librecms.contentsection.ContentItem;
import org.librecms.contenttypes.Event; import org.librecms.contenttypes.Event;
import java.util.Locale; import java.util.Locale;
@ -28,13 +29,20 @@ import java.util.Map;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ContentItemRenderer(renders = Event.class) @ContentItemRenderer(renders = Event.class)
public class EventRenderer extends AbstractContentItemRenderer<Event> { public class EventRenderer extends AbstractContentItemRenderer {
@Override @Override
public void renderItem(final Event event, public void renderItem(final ContentItem item,
final Locale language, final Locale language,
final Map<String, Object> result) { final Map<String, Object> result) {
final Event event;
if (item instanceof Event) {
event = (Event) item;
} else {
return;
}
result.put("text", event.getText().getValue(language)); result.put("text", event.getText().getValue(language));
result.put("startDate", event.getStartDate()); result.put("startDate", event.getStartDate());
result.put("endDate", event.getEndDate()); result.put("endDate", event.getEndDate());

View File

@ -18,6 +18,7 @@
*/ */
package org.librecms.pagemodel.contentitems; package org.librecms.pagemodel.contentitems;
import org.librecms.contentsection.ContentItem;
import org.librecms.contenttypes.MultiPartArticle; import org.librecms.contenttypes.MultiPartArticle;
import org.librecms.contenttypes.MultiPartArticleSection; import org.librecms.contenttypes.MultiPartArticleSection;
@ -31,14 +32,20 @@ import java.util.stream.Collectors;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ContentItemRenderer(renders = MultiPartArticle.class) @ContentItemRenderer(renders = MultiPartArticle.class)
public class MultiPartArticleRenderer public class MultiPartArticleRenderer extends AbstractContentItemRenderer {
extends AbstractContentItemRenderer<MultiPartArticle> {
@Override @Override
public void renderItem(final MultiPartArticle article, public void renderItem(final ContentItem item,
final Locale language, final Locale language,
final Map<String, Object> result) { final Map<String, Object> result) {
final MultiPartArticle article;
if (item instanceof MultiPartArticle) {
article = (MultiPartArticle) item;
} else {
return;
}
result.put("summary", article.getSummary().getValue(language)); result.put("summary", article.getSummary().getValue(language));
result.put("sections", result.put("sections",
article article

View File

@ -18,6 +18,7 @@
*/ */
package org.librecms.pagemodel.contentitems; package org.librecms.pagemodel.contentitems;
import org.librecms.contentsection.ContentItem;
import org.librecms.contenttypes.News; import org.librecms.contenttypes.News;
import java.util.Locale; import java.util.Locale;
@ -31,13 +32,20 @@ import javax.enterprise.context.RequestScoped;
*/ */
@ContentItemRenderer(renders = News.class) @ContentItemRenderer(renders = News.class)
@RequestScoped @RequestScoped
public class NewsRenderer extends AbstractContentItemRenderer<News> { public class NewsRenderer extends AbstractContentItemRenderer {
@Override @Override
public void renderItem(final News news, public void renderItem(final ContentItem item,
final Locale language, final Locale language,
final Map<String, Object> result) { final Map<String, Object> result) {
final News news;
if (item instanceof News) {
news = (News) item;
} else {
return;
}
result.put("text", news.getText().getValue(language)); result.put("text", news.getText().getValue(language));
result.put("releaseDate", news.getReleaseDate()); result.put("releaseDate", news.getReleaseDate());
} }