package com.psddev.cms.image; import com.google.common.base.MoreObjects; import com.psddev.cms.db.ImageTag; import com.psddev.cms.view.ViewModel; import com.psddev.dari.util.ObjectUtils; import com.psddev.dari.util.StorageItem; import com.psddev.dari.util.ThreadLocalStack; import java.util.ArrayList; import java.util.List; /** * Image sizing information. */ public final class ImageSize { private static final ThreadLocalStack<ImageSizeProvider> PROVIDER = new ThreadLocalStack<>(); private static final ThreadLocalStack<String> FIELD = new ThreadLocalStack<>(); private static final ThreadLocal<List<String>> CONTEXTS = ThreadLocal.withInitial(ArrayList::new); private final int width; private final int height; /** * Returns an instance most appropriate for use with the given * {@code field} in the current contexts. * * @param field Nullable. * @return Nullable. */ public static ImageSize getInstance(String field) { ImageSizeProvider provider = PROVIDER.get(); return provider != null ? provider.get(CONTEXTS.get(), field) : null; } /** * Returns a URL to the given {@code image}, sized most appropriately for * use with the given {@code field} in the current contexts. * * @param image Nullable. * @param field Nullable. * @return Nullable. */ public static String getUrlForField(StorageItem image, String field) { if (image == null) { return null; } ImageSize size = getInstance(field); if (size != null) { return new ImageTag.Builder(image) .setWidth(size.getWidth()) .setHeight(size.getHeight()) .toAttributes() .get("src"); } else { return image.getPublicUrl(); } } /** * Returns a URL to the given {@code image}, sized most appropriate for * use in the current contexts. * * @param image Nullable. * @return Nullable. */ public static String getUrl(StorageItem image) { if (image == null) { return null; } String field = FIELD.get(); if (field == null) { for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { Class<?> c = ObjectUtils.getClassByName(ste.getClassName()); if (c != null && ViewModel.class.isAssignableFrom(c)) { field = ImageSizeEnhancer.toField(ste.getMethodName()); if (field != null) { break; } } } } return field != null ? getUrlForField(image, field) : image.getPublicUrl(); } /** * Returns the stack that points to the current image size provider. * * @return Nonnull. */ public static ThreadLocalStack<ImageSizeProvider> getProviderStack() { return PROVIDER; } /** * Returns the stack that points to the current field. * * @return Nonnull. */ public static ThreadLocalStack<String> getFieldStack() { return FIELD; } /** * Adds the given {@code context}. * * @param context Nullable. */ public static void pushContext(String context) { CONTEXTS.get().add(context); } /** * Removes the lastly added context. */ public static void popContext() { List<String> contexts = CONTEXTS.get(); if (!contexts.isEmpty()) { contexts.remove(contexts.size() - 1); } } /** * Clears all contexts. */ public static void clearContexts() { CONTEXTS.remove(); } /** * Returns a builder that can be used to create an instance. * * @return Nonnull. */ public static ImageSizeBuilder builder() { return new ImageSizeBuilder(); } ImageSize(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } @Override public boolean equals(Object other) { if (this == other) { return true; } else if (other instanceof ImageSize) { ImageSize otherSize = (ImageSize) other; return width == otherSize.width && height == otherSize.height; } else { return false; } } @Override public int hashCode() { return ObjectUtils.hashCode(width, height); } @Override public String toString() { return MoreObjects .toStringHelper(this) .add("width", getWidth()) .add("height", getHeight()) .toString(); } }