All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.cq.social.scf.core.BaseSocialComponent Maven / Gradle / Ivy

There is a newer version: 6.5.21
Show newest version
/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2013 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.cq.social.scf.core;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;

import com.adobe.cq.social.scf.ClientUtilities;
import com.adobe.cq.social.scf.JsonException;
import com.adobe.cq.social.scf.SocialComponent;
import com.adobe.cq.social.ugcbase.SocialUtils;
import com.day.cq.wcm.api.Page;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

/**
 * Default SocialComponent implementation for all Resource Types. This class should be extended to implement
 * SocialComponent for other Resource Types.
 */
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, getterVisibility = Visibility.PUBLIC_ONLY,
        isGetterVisibility = Visibility.PUBLIC_ONLY, setterVisibility = Visibility.NONE)
@JsonInclude(Include.NON_EMPTY)
public class BaseSocialComponent implements SocialComponent {

    private static ObjectMapper objectMapper;
    private static Charset UTF8_CHARSET = Charset.forName("UTF-8");

    protected final Resource resource;
    protected final ResourceID id;
    private volatile Map properties = null;

    static {
        objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true).configure(
            SerializationFeature.WRAP_EXCEPTIONS, false);
    }

    /**
     * Instance of ClientUtilities for accessing helper methods for XSS support and generating URLs.
     */
    protected ClientUtilities clientUtils;

    /**
     * A list of properties that will be omitted when you getProperties for the resource. The default will omit
     * 'cq.*', 'ip', 'email', 'userAgent'. This list can contain regex.
     */
    protected final List ignoredProperties = new ArrayList(Arrays.asList("cq:.*", "ip", "email",
        "userAgent", "sling:.*"));
    protected List ignoredPropertiesPatterns;

    private final List whiteList = Arrays.asList("cq:tags");

    /**
     * @param resource the resource for which this BaseSocialComponent will be created for.
     * @param clientUtils an instance of ClientUtilities for accessing helper methods for XSS support and generating
     *            URLs.
     */
    public BaseSocialComponent(final Resource resource, final ClientUtilities clientUtils) {
        this.resource = resource;
        this.id = new DefaultResourceID(resource);
        this.clientUtils = clientUtils;
    }

    public static String buildJSONString(final Map data, final boolean tidy) throws JsonException {
        try {
            final JsonFactory f = new JsonFactory();
            f.setCharacterEscapes(new EscapeCloseScriptBlocks());
            final ByteArrayOutputStream bastream = new ByteArrayOutputStream();
            final JsonGenerator jgen = f.createGenerator(bastream, JsonEncoding.UTF8);
            if (tidy) {
                objectMapper.writerWithDefaultPrettyPrinter().writeValue(jgen, data);
            } else {
                objectMapper.writeValue(jgen, data);
            }
            return new String(bastream.toByteArray(), UTF8_CHARSET);
        } catch (final JsonProcessingException e) {
            throw new JsonException("Error converting map to JSON", e);
        } catch (final IOException e) {
            throw new JsonException("Error converting map to JSON", e);
        }
    }

    @Override
    public ResourceID getId() {
        return this.id;
    }

    @Override
    public String getResourceType() {
        return this.resource.getResourceType();
    }

    @Override
    public String getUrl() {
        return this.externalizeURL(this.resource.getPath());
    }

    @Override
    public Map getProperties() {
        if (properties == null) {
            synchronized (this) {
                if (properties == null) {
                    Map tmpMap = new HashMap();
                    final BaseSocialComponent thiz = BaseSocialComponent.this;
                    final ValueMap propMap = thiz.resource.adaptTo(ValueMap.class);
                    if (propMap != null) {
                        initIgnoredPropertiesList();
                        for (final Entry entry : propMap.entrySet()) {
                            if (!thiz.isKeyIgnored(entry.getKey())) {
                                final Object value = entry.getValue();
                                if (value == null) {
                                    tmpMap.put(entry.getKey(), "");
                                } else {
                                    tmpMap.put(entry.getKey(), value);
                                }
                            }
                        }
                    }
                    this.properties = tmpMap;
                }
            }
        }
        return properties;
    }

    /**
     * Get the resource value for the specified key.
     */
    public  T getProperty(String name, Class type) {
        Map props = getProperties();
        return convert(props.get(name), type);
    }

    /**
     * Get the resource value for the specified key, if the property doesn't exist, return the specified default
     * value.
     * @param name the name of the property
     * @param defaultValue a non null default value to be returned by this method if the specified property does not
     *            exist
     */
    public  T getProperty(String name, T defaultValue) {
        T value = getProperty(name, (Class) defaultValue.getClass());
        return value == null ? defaultValue : value;
    }

    private  T convert(Object obj, Class type) {
        try {
            if (obj == null) {
                return null;
            } else if (type.isAssignableFrom(obj.getClass())) {
                return (T) obj;
            } else if (type.isArray()) {
                return (T) convertToArray(obj, type.getComponentType());
            } else if (type == String.class) {
                return (T) String.valueOf(obj);
            } else if (type == Integer.class) {
                return (T) (Integer) Integer.parseInt(obj.toString());
            } else if (type == Long.class) {
                return (T) (Long) Long.parseLong(obj.toString());
            } else if (type == Double.class) {
                return (T) (Double) Double.parseDouble(obj.toString());
            } else if (type == Boolean.class) {
                return (T) (Boolean) Boolean.parseBoolean(obj.toString());
            } else {
                return null;
            }
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Converts the object to an array of the given type
     * @param obj tje object or object array
     * @param type the component type of the array
     * @return and array of type T
     */
    private  T[] convertToArray(Object obj, Class type) {
        List values = new LinkedList();
        if (obj.getClass().isArray()) {
            for (Object o : (Object[]) obj) {
                T ct = convert(o, type);
                if (ct != null) {
                    values.add(ct);
                }
            }
        } else {
            T ct = convert(obj, type);
            if (ct != null) {
                values.add(ct);
            }
        }
        @SuppressWarnings("unchecked")
        T[] result = (T[]) Array.newInstance(type, values.size());
        return values.toArray(result);
    }

    private void initIgnoredPropertiesList() {
        ignoredPropertiesPatterns = new ArrayList(getIgnoredProperties().size());
        for (final String ignoredKey : this.getIgnoredProperties()) {
            ignoredPropertiesPatterns.add(Pattern.compile(ignoredKey));
        }

    }

    /**
     * This method determines whether a property should be omitted or not by checking against the ignore list.
     * @param key property name
     * @return true if this property is to be omitted, false otherwise.
     */
    protected boolean isKeyIgnored(final String key) {
        for (final Pattern ignoredKey : ignoredPropertiesPatterns) {
            if (ignoredKey.matcher(key).matches() && !whiteList.contains(key)) {
                return true;
            }
        }
        return false;
    }

    /**
     * This method returns the list of property names/regex that need to be omitted. Override this method to return a
     * more comprehensive list specific to your component.
     * @return a list of property names/regex that need to be omitted.
     */
    protected List getIgnoredProperties() {
        return this.ignoredProperties;
    }

    @Override
    @JsonIgnore
    public String toJSONString(final boolean tidy) throws JsonException {
        try {
            final JsonFactory f = new JsonFactory();
            f.setCharacterEscapes(new EscapeCloseScriptBlocks());
            final ByteArrayOutputStream bastream = new ByteArrayOutputStream();
            final JsonGenerator jgen = f.createGenerator(bastream, JsonEncoding.UTF8);
            if (tidy) {
                objectMapper.writerWithDefaultPrettyPrinter().writeValue(jgen, this);
            } else {
                objectMapper.writeValue(jgen, this);
            }
            return new String(bastream.toByteArray(), UTF8_CHARSET);
        } catch (final JsonProcessingException e) {
            throw new JsonException("Error converting " + this.id + " to JSON", e);
        } catch (final IOException e) {
            throw new JsonException("Error converting " + this.id + " to JSON", e);
        }
    }

    @Override
    @JsonIgnore
    public Resource getResource() {
        return this.resource;
    }

    /**
     * @param path absolute path to the resource
     * @return an externalized URL to the resource
     */
    protected String externalizeURL(final String path) {
        if (this.clientUtils == null) {
            return path;
        }
        return this.clientUtils.externalLink(path);
    }

    @Override
    @SuppressWarnings("unchecked")
    @JsonIgnore
    public Map getAsMap() {
        return objectMapper.convertValue(this, Map.class);
    }

    @Override
    public SocialComponent getParentComponent() {
        return null;
    }

    @Override
    public SocialComponent getSourceComponent() {
        return this;
    }

    @Override
    public String getFriendlyUrl() {
        final SocialUtils socialUtils = clientUtils.getSocialUtils();
        if (socialUtils != null) {
            final Page page = socialUtils.getContainingPage(resource);
            if (page != null) {
                return clientUtils.externalLink(page.getPath(), false) + ".html";
            }

        }
        return null;
    }

    static private class EscapeCloseScriptBlocks extends CharacterEscapes {
        /**
         *
         */
        private static final long serialVersionUID = 1L;
        private final int[] escapes;

        public EscapeCloseScriptBlocks() {
            final int[] baseEscapes = standardAsciiEscapesForJSON();
            baseEscapes['<'] = CharacterEscapes.ESCAPE_STANDARD;
            baseEscapes['>'] = CharacterEscapes.ESCAPE_STANDARD;
            escapes = baseEscapes;
        }

        @Override
        public int[] getEscapeCodesForAscii() {
            return escapes;
        }

        @Override
        public SerializableString getEscapeSequence(final int arg0) {
            return null;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy