/**
* Copyright (C) 2010 eXo Platform SAS.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xcmis.spi;
import org.xcmis.spi.model.BaseType;
import org.xcmis.spi.model.Rendition;
import org.xcmis.spi.utils.CmisUtils;
import org.xcmis.spi.utils.Logger;
import org.xcmis.spi.utils.MimeType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
/**
* Manage object's renditions.
*
* @author <a href="mailto:andrey.parfonov@exoplatform.com">Andrey Parfonov</a>
* @version $Id$
*/
public class RenditionManager
{
private static final Logger LOG = Logger.getLogger(RenditionManager.class);
private static AtomicReference<RenditionManager> manager = new AtomicReference<RenditionManager>();
public static RenditionManager getInstance()
{
RenditionManager s = manager.get();
if (s == null)
{
manager.compareAndSet(null, new RenditionManager());
s = manager.get();
}
return s;
}
public static void setInstance(RenditionManager inst)
{
manager.set(inst);
}
/**
* Decode string from hex-string.
*
* @param in the input string
* @return string output
*/
private static String decode(String in)
{
StringBuffer out = new StringBuffer();
int offset = 0;
while (offset < in.length())
{
int part = Integer.parseInt(in.substring(offset, offset + 2), 16);
out.append((char)part);
offset = offset + 2;
}
return out.toString();
}
/**
* Encode string into hex-string.
*
* @param in the input string
* @return the string hex-sequence
*/
private static String encode(String in)
{
StringBuffer out = new StringBuffer();
for (int i = 0; i < in.length(); i++)
{
out.append(Integer.toHexString(in.charAt(i)));
}
return out.toString();
}
protected Map<MimeType, RenditionProvider> renditionProviders =
new TreeMap<MimeType, RenditionProvider>(new Comparator<MimeType>()
{
public int compare(MimeType m1, MimeType m2)
{
if (m1.getType().equals(CmisConstants.WILDCARD) && !m2.getType().equals(CmisConstants.WILDCARD))
{
return 1;
}
if (!m1.getType().equals(CmisConstants.WILDCARD) && m2.getType().equals(CmisConstants.WILDCARD))
{
return -1;
}
if (m1.getSubType().equals(CmisConstants.WILDCARD) && !m2.getSubType().equals(CmisConstants.WILDCARD))
{
return 1;
}
if (!m1.getSubType().equals(CmisConstants.WILDCARD) && m2.getSubType().equals(CmisConstants.WILDCARD))
{
return -1;
}
return m1.toString().compareToIgnoreCase(m2.toString());
}
});
protected RenditionManager()
{
}
public void addRenditionProviders(List<String> provs)
{
if (provs != null && !provs.isEmpty())
{
for (String one : provs)
{
try
{
RenditionProvider prov = (RenditionProvider)Class.forName(one).newInstance();
for (MimeType mimeType : prov.getSupportedMediaType())
{
renditionProviders.put(mimeType, prov);
}
}
catch (Exception e)
{
LOG.error("Cannot instantiate rendition provider instance: ", e);
}
}
}
}
/**
* Get all renditions using all available {@link RenditionProvider} that can
* provide rendition for specified mime-type.
*
* @param @param mime MimeType
* @return set of object renditions. If object has not renditions then empty
* iterator will be returned
* @throws NullPointerException if <code>mime == null</code>
*/
public ItemsIterator<Rendition> getRenditions(MimeType mime)
{
if (mime == null)
{
throw new NullPointerException("Mime-type may not be null.");
}
List<Rendition> renditions = new ArrayList<Rendition>();
for (Map.Entry<MimeType, RenditionProvider> e : renditionProviders.entrySet())
{
if (e.getKey().match(mime))
{
Rendition rendition = new Rendition();
// e.getKey() is unique because is key of map.
// Use it as id for content stream.
rendition.setStreamId(encode(e.getKey().toString()));
rendition.setKind(e.getValue().getKind());
rendition.setMimeType(e.getValue().getProducedMediaType().toString());
renditions.add(rendition);
}
}
return new BaseItemsIterator<Rendition>(renditions);
}
/**
* Get all renditions of specified entry.
*
* @param object object for getting renditions
* @return set of object renditions. If object has not renditions then empty
* iterator will be returned
*/
public ItemsIterator<Rendition> getRenditions(ObjectData object)
{
// Not support other than document objects
if (object.getBaseType() == BaseType.DOCUMENT && ((DocumentData)object).hasContent())
{
MimeType mime = MimeType.fromString(((DocumentData)object).getContentStreamMimeType());
return getRenditions(mime);
}
return CmisUtils.emptyItemsIterator();
}
/**
* Get rendition from content stream with known mime-type and use most
* suitable {@link RenditionProvider}. For example if two RenditionProviders
* registered:
* <ul>
* <li>can process 'image/*' content</li>
* <li>can process 'image/jpeg' content</li>
* </ul>
* and provided MimeType is 'image/jpeg' then second provider from list will
* be in use.
*
* @param mime MimeType
* @param stream ContentStream
* @return rendition content stream or <code>null</code> if there is no
* {@link RenditionProvider} which can produce stream for requested
* type
* @throws IOException if any I/O error occurs
*/
public RenditionContentStream getStream(ContentStream stream, MimeType mime) throws IOException
{
for (Map.Entry<MimeType, RenditionProvider> e : renditionProviders.entrySet())
{
if (e.getKey().match(mime))
{
RenditionProvider renditionProvider = e.getValue();
RenditionContentStream renditionContentStream = renditionProvider.getRenditionStream(stream);
return renditionContentStream;
}
}
return null;
}
/**
* Get rendition stream for objects with specified stream id.
*
* @param streamId stream id
* @param obj ObjectData
* @return Renditions content stream
* @throws IOException if any I/O error occurs
*/
public RenditionContentStream getStream(ObjectData object, String streamId) throws IOException
{
// Assume streamId is encoded produces mime-type of RenditionProvider.
// See RenditionProvider#getProducedMediaType().
// Not support other than document objects
if (object.getBaseType() == BaseType.DOCUMENT && ((DocumentData)object).hasContent())
{
MimeType mime = MimeType.fromString(decode(streamId));
return getStream(((DocumentData)object).getContentStream(), mime);
}
return null;
}
}