001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.apps.cacheadmin;
029
030import com.alkacon.simapi.Simapi;
031
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProject;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.loader.CmsImageLoader;
037import org.opencms.main.CmsException;
038import org.opencms.main.OpenCms;
039import org.opencms.util.CmsFileUtil;
040import org.opencms.util.CmsStringUtil;
041
042import java.awt.image.BufferedImage;
043import java.io.File;
044import java.io.IOException;
045import java.util.ArrayList;
046import java.util.Collections;
047import java.util.HashMap;
048import java.util.List;
049import java.util.Map;
050
051/**
052 * Image Cache helper.<p>
053 *
054 * @since 7.0.5
055 */
056public class CmsImageCacheHelper {
057
058    /**File path map. */
059    private Map<String, String> m_filePaths = new HashMap<String, String>();
060
061    /** Lengths map. */
062    private Map m_lengths = new HashMap();
063
064    /** Sizes map. */
065    private Map m_sizes = new HashMap();
066
067    /** Variations map. */
068    private Map m_variations = new HashMap();
069
070    /** The total number of variations. */
071    private int m_variationsCount;
072
073    /** The total size of all variations. */
074    private int m_variationsSize;
075
076    /**
077     * Default constructor.<p>
078     *
079     * @param cms the cms context
080     * @param withVariations if also variations should be read
081     * @param showSize if it is needed to compute the image size
082     * @param statsOnly if only statistical information should be retrieved
083     */
084    public CmsImageCacheHelper(CmsObject cms, boolean withVariations, boolean showSize, boolean statsOnly) {
085
086        init(cms, withVariations, showSize, statsOnly);
087    }
088
089    /**
090     * Returns all cached images.<p>
091     *
092     * @return a list of root paths
093     */
094    public List getAllCachedImages() {
095
096        List ret = new ArrayList(m_variations.keySet());
097        Collections.sort(ret);
098        return ret;
099    }
100
101    /**
102     * Returns the total number of files.<p>
103     *
104     * @return the total number of files
105     */
106    public int getFilesCount() {
107
108        return m_variations.keySet().size();
109    }
110
111    /**
112     * Returns the length of the given image.<p>
113     *
114     * @param imgName the image name
115     *
116     * @return the length of the given image
117     */
118    public String getLength(String imgName) {
119
120        String ret = (String)m_lengths.get(imgName);
121        if (ret == null) {
122            return "";
123        }
124        return ret;
125    }
126
127    /**
128     * Reads the size of a single image.<p>
129     *
130     * @param cms CmsObejct
131     * @param resPath Path of image (uri)
132     * @return a String representation of the dimension of the given image
133     * @throws CmsException if something goes wrong
134     */
135    public String getSingleSize(CmsObject cms, String resPath) throws CmsException {
136
137        CmsResource res = getClonedCmsObject(cms).readResource(resPath);
138        return getSingleSize(cms, res);
139    }
140
141    /**
142     * Returns the size of the given image.<p>
143     *
144     * @param imgName the image name
145     *
146     * @return the size of the given image
147     */
148    public String getSize(String imgName) {
149
150        String ret = (String)m_sizes.get(imgName);
151        if (ret == null) {
152            return "";
153        }
154        return ret;
155    }
156
157    /**
158     * Returns the variations for the given image.<p>
159     *
160     * @param imgName the image name
161     *
162     * @return the variations for the given image
163     */
164    public List getVariations(String imgName) {
165
166        List ret = (List)m_variations.get(imgName);
167        if (ret == null) {
168            return new ArrayList();
169        }
170        Collections.sort(ret);
171        return ret;
172    }
173
174    /**
175     * Returns the total number of variations.<p>
176     *
177     * @return the total number of variations
178     */
179    public int getVariationsCount() {
180
181        return m_variationsCount;
182    }
183
184    /**
185     * Returns the total size of all variations.<p>
186     *
187     * @return the total size of all variations
188     */
189    public int getVariationsSize() {
190
191        return m_variationsSize;
192    }
193
194    /**
195     * Reads out the path of a given image (by its name).<p>
196     *
197     * @param resName name of image
198     * @return path to it on Server
199     */
200    String getFilePath(String resName) {
201
202        return m_filePaths.get(resName);
203    }
204
205    /**
206     * Clones a CmsObject.<p>
207     *
208     * @param cms the CmsObject to be cloned.
209     * @return a clones CmsObject
210     * @throws CmsException if something goes wrong
211     */
212    private CmsObject getClonedCmsObject(CmsObject cms) throws CmsException {
213
214        CmsObject clonedCms = OpenCms.initCmsObject(cms);
215        // only online images get caches
216        clonedCms.getRequestContext().setCurrentProject(clonedCms.readProject(CmsProject.ONLINE_PROJECT_ID));
217        // paths are always root path
218        clonedCms.getRequestContext().setSiteRoot("");
219
220        return clonedCms;
221    }
222
223    /**
224     * Reads the size of a single image.<p>
225     *
226     * @param cms CmsObejct
227     * @param res CmsResource to be read
228     * @return a String representation of the dimension of the given image
229     */
230    private String getSingleSize(CmsObject cms, CmsResource res) {
231
232        try {
233            BufferedImage img = Simapi.read(cms.readFile(res).getContents());
234            return "" + img.getWidth() + " x " + img.getHeight() + "px";
235        } catch (CmsException | IOException e) {
236            return "";
237        }
238    }
239
240    /**
241     * Reads all cached images.<p>
242     *
243     * @param cms the cms context
244     * @param withVariations if also variations should be read
245     * @param showSize if it is needed to compute the image size
246     * @param statsOnly if only statistical information should be retrieved
247     */
248    private void init(CmsObject cms, boolean withVariations, boolean showSize, boolean statsOnly) {
249
250        File basedir = new File(CmsImageLoader.getImageRepositoryPath());
251        try {
252            CmsObject clonedCms = getClonedCmsObject(cms);
253            visitImages(clonedCms, basedir, withVariations, showSize, statsOnly);
254        } catch (CmsException e) {
255            // should never happen
256        }
257        m_variations = Collections.unmodifiableMap(m_variations);
258        m_sizes = Collections.unmodifiableMap(m_sizes);
259        m_lengths = Collections.unmodifiableMap(m_lengths);
260    }
261
262    /**
263     * Visits a single image.<p>
264     *
265     * @param cms CmsObject
266     * @param f a File to be read out
267     * @param withVariations boolean
268     * @param showSize boolean
269     * @param statsOnly boolean
270     */
271    private void visitImage(CmsObject cms, File f, boolean withVariations, boolean showSize, boolean statsOnly) {
272
273        m_variationsCount++;
274        m_variationsSize += f.length();
275        String oName = f.getAbsolutePath().substring(CmsImageLoader.getImageRepositoryPath().length());
276        oName = CmsStringUtil.substitute(oName, "\\", "/");
277        if (!oName.startsWith("/")) {
278            oName = "/" + oName;
279        }
280        String imgName = oName;
281        CmsResource res = null;
282        boolean found = false;
283        while (!found) {
284            String path = CmsResource.getParentFolder(imgName);
285            String name = imgName.substring(path.length());
286            String ext = CmsFileUtil.getExtension(imgName);
287            String nameWoExt = name.substring(0, name.length() - ext.length());
288            int pos = nameWoExt.lastIndexOf("_");
289            String newName = path;
290            found = (pos < 0);
291            if (!found) {
292                newName += nameWoExt.substring(0, pos);
293            } else {
294                newName += nameWoExt;
295            }
296            newName += ext;
297            try {
298                res = cms.readResource(newName, CmsResourceFilter.ALL);
299                found = true;
300            } catch (Exception e) {
301                // it could be a variation
302            }
303            imgName = newName;
304        }
305
306        if (res != null) {
307            oName = res.getRootPath();
308        }
309        m_filePaths.put(oName, f.getAbsolutePath());
310        List variations = (List)m_variations.get(oName);
311        if (variations == null) {
312            variations = new ArrayList();
313            m_variations.put(oName, variations);
314            if (statsOnly) {
315                return;
316            }
317            if (res != null) {
318                m_lengths.put(oName, "" + res.getLength() + " Bytes");
319                if (showSize) {
320                    m_sizes.put(oName, getSingleSize(cms, res));
321                }
322            } else {
323                m_lengths.put(oName, "" + f.length() + " Bytes");
324                if (showSize) {
325                    try {
326                        BufferedImage img = Simapi.read(f);
327                        m_sizes.put(oName, "" + img.getWidth() + " x " + img.getHeight() + "px");
328                    } catch (Throwable e) {
329                        // ignore
330                    }
331                }
332            }
333        }
334        if (!withVariations) {
335            return;
336        }
337        oName += " (";
338        if (showSize) {
339            try {
340                BufferedImage img = Simapi.read(f);
341                oName += "" + img.getWidth() + " x " + img.getHeight() + "px - ";
342            } catch (Throwable e) {
343                // ignore
344            }
345        }
346        oName += f.length() + " Bytes)";
347        variations.add(oName);
348    }
349
350    /**
351    * Visits all cached images in the given directory.<p>
352    *
353    * @param cms the cms context
354    * @param directory the directory to visit
355    * @param withVariations if also variations should be read
356    * @param showSize if it is needed to compute the image size
357    * @param statsOnly if only statistical information should be retrieved
358    */
359    private void visitImages(
360        CmsObject cms,
361        File directory,
362        boolean withVariations,
363        boolean showSize,
364        boolean statsOnly) {
365
366        if (!directory.canRead() || !directory.isDirectory()) {
367            return;
368        }
369        File[] files = directory.listFiles();
370        for (int i = 0; i < files.length; i++) {
371            File f = files[i];
372            if (f.isDirectory()) {
373                visitImages(cms, f, withVariations, showSize, statsOnly);
374                continue;
375            }
376            visitImage(cms, f, withVariations, showSize, statsOnly);
377        }
378    }
379
380}