/* * Universal Media Server, for streaming any media to DLNA compatible renderers * based on the http://www.ps3mediaserver.org. Copyright (C) 2012 UMS * developers. * * This program is a free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; version 2 of the License only. * * This program 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.pms.dlna.protocolinfo; import static org.apache.commons.lang3.StringUtils.isBlank; import java.io.Serializable; import org.fourthline.cling.support.model.Protocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.pms.dlna.protocolinfo.ProfileName.DefaultGenericProfileName; import net.pms.util.ParseException; /** * This interface represents attributes found in the {@code additionalInfo} part * of {@code protocolInfo}. * * @author Nadahar */ public interface ProtocolInfoAttribute extends Serializable { /** * The static factory singleton instance for creating or retrieve * predefined and cached {@link ProtocolInfoAttribute} instances. */ ProtocolInfoAttributeFactory FACTORY = new ProtocolInfoAttributeFactory(); /** * @return The {@link ProtocolInfoAttributeName} instance for this * {@link ProtocolInfoAttribute}. */ ProtocolInfoAttributeName getName(); /** * @return The {@link String} value of this {@link ProtocolInfoAttribute}'s * {@link ProtocolInfoAttributeName} instance. */ String getNameString(); /** * @return The {@link String} value of this {@link ProtocolInfoAttribute}. */ String getValue(); /** * Returns a formatted attribute string for use in {@code protocolInfo}. If * this {@link ProtocolInfoAttribute} has an empty name or value, or * represents the implied default, an empty string is returned. * * @return The formatted attribute or an empty {@link String}. */ String getAttributeString(); /** * A factory for creating or retrieving cached * {@link ProtocolInfoAttribute} instances. */ public static class ProtocolInfoAttributeFactory { /** The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolInfoAttributeFactory.class); /** * For internal use only, use {@link ProtocolInfoAttribute#FACTORY} * instead. */ protected ProtocolInfoAttributeFactory() { } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, {@code null} * is returned. * <p> * <b>{@link DLNAOrgOperations} can't be retrieved with this method and * will throw an {@link IllegalArgumentException}</b>, use * {@link #getAttribute(String, String, Protocol)} instead. * <p> * <b>Note:</b> {@link DLNAOrgFlags} isn't predefined or cached and * will always return {@code null}. * <p> * This method can only retrieve values in their {@link String} form. * Use the factory methods of the individual classes to retrieve * instances for values of other types. * * @param attributeName the attribute name {@link String} value. * @param attributeValue the attribute {@link String} value. * @return The existing instance or {@code null}. * @throws ParseException If {@code attributeValue} can't be parsed. * @throws IllegalArgumentException If {@code attributeName} is * {@code "DLNA.ORG_OP"}. */ public ProtocolInfoAttribute getAttribute( String attributeName, String attributeValue ) throws ParseException { return getAttribute( ProtocolInfoAttributeName.FACTORY.createAttributeName(attributeName), attributeValue ); } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, {@code null} * is returned. * <p> * <b>{@link DLNAOrgOperations} can't be retrieved with this method and * will throw an {@link IllegalArgumentException}</b>, use * {@link #getAttribute(ProtocolInfoAttributeName, String, Protocol)} * instead. * <p> * <b>Note:</b> {@link DLNAOrgFlags} isn't predefined or cached and * will always return {@code null}. * <p> * This method can only retrieve values in their {@link String} form. * Use the factory methods of the individual classes to retrieve * instances for values of other types. * * @param attributeName the {@link ProtocolInfoAttributeName}. * @param attributeValue the attribute {@link String} value. * @return The existing instance or {@code null}. * @throws ParseException If {@code attributeValue} can't be parsed. * @throws IllegalArgumentException If {@code attributeName} is * {@link DLNAOrgOperations#NAME}. */ public ProtocolInfoAttribute getAttribute( ProtocolInfoAttributeName attributeName, String attributeValue ) throws ParseException { if (DLNAOrgOperations.NAME.equals(attributeName)) { throw new IllegalArgumentException( "Cannot get DLNA.ORG_FLAGS instance because protocol information is needed " + "- use an overloaded version of this method which takes a protocol argument"); } return getAttribute(attributeName, attributeValue, null); } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, {@code null} * is returned. * <p> * <b>Note:</b> {@link DLNAOrgFlags} isn't predefined or cached and * will always return {@code null}. * <p> * This method can only retrieve values in their {@link String} form. * Use the factory methods of the individual classes to retrieve * instances for values of other types. * * @param attributeName the attribute name {@link String} value. * @param attributeValue the attribute {@link String} value. * @param protocol the {@link Protocol} of the {@code protocolInfo} this * {@link ProtocolInfoAttribute} belongs to. This parameter * is only used when retrieving {@link DLNAOrgOperations} * instances and can be {@code null} for other attribute * types. * @return The existing instance or {@code null}. * @throws ParseException If {@code attributeValue} can't be parsed. */ public ProtocolInfoAttribute getAttribute( String attributeName, String attributeValue, Protocol protocol ) throws ParseException { return getAttribute( ProtocolInfoAttributeName.FACTORY.createAttributeName(attributeName), attributeValue, protocol ); } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, {@code null} * is returned. * <p> * <b>Note:</b> {@link DLNAOrgFlags} isn't predefined or cached and * will always return {@code null}. * <p> * This method can only retrieve values in their {@link String} form. * Use the factory methods of the individual classes to retrieve * instances for values of other types. * * @param attributeName the {@link ProtocolInfoAttributeName}. * @param attributeValue the attribute {@link String} value. * @param protocol the {@link Protocol} of the {@code protocolInfo} this * {@link ProtocolInfoAttribute} belongs to. This parameter * is only used when retrieving {@link DLNAOrgOperations} * instances and can be {@code null} for other attribute * types. * @return The existing instance or {@code null}. * @throws ParseException If {@code attributeValue} can't be parsed. */ public ProtocolInfoAttribute getAttribute( ProtocolInfoAttributeName attributeName, String attributeValue, Protocol protocol ) throws ParseException { if (attributeName == null) { return null; } /* Check predefined and cached types, the following classes don't have * predefined or cached types and will always return null: * * - DLNAOrgFlags * */ if (DLNAOrgConversionIndicator.NAME.equals(attributeName)) { return DLNAOrgConversionIndicator.FACTORY.getConversionIndicator(attributeValue); } else if (DLNAOrgOperations.NAME.equals(attributeName)) { return DLNAOrgOperations.FACTORY.getOperations(protocol, attributeValue); } else if (DLNAOrgPlaySpeeds.NAME.equals(attributeName)) { return DLNAOrgPlaySpeeds.FACTORY.getPlaySpeeds(attributeValue); } else if (DLNAOrgProfileName.NAME.equals(attributeName)) { return DLNAOrgProfileName.FACTORY.getProfileName(attributeValue); } else if (PanasonicComProfileName.NAME.equals(attributeName)) { return PanasonicComProfileName.FACTORY.getProfileName(attributeValue); } else if (AribOrJpProfileName.NAME.equals(attributeName)) { return AribOrJpProfileName.FACTORY.getProfileName(attributeValue); } else if (attributeName.getName().contains("_PN")) { return DefaultGenericProfileName.FACTORY.getProfileName(attributeName, attributeValue); } return null; } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, a new will * be created. * <p> * <b>{@link DLNAOrgOperations} can't be retrieved with this method and * will throw an {@link IllegalArgumentException}</b>, use * {@link #createAttribute(String, String, Protocol)} instead. * <p> * This method can only retrieve or create values in their * {@link String} form. Use the factory methods of the individual * classes to retrieve or create instances for values of other types. * * @param attributeName the attribute name {@link String} value. * @param attributeValue the attribute {@link String} value. * @return The existing or created instance. * @throws ParseException If {@code attributeValue} can't be parsed. * @throws IllegalArgumentException If {@code attributeName} is * {@code "DLNA.ORG_OP"}. */ public ProtocolInfoAttribute createAttribute( String attributeName, String attributeValue ) throws ParseException { return createAttribute( ProtocolInfoAttributeName.FACTORY.createAttributeName(attributeName), attributeValue ); } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, a new will * be created. * <p> * <b>{@link DLNAOrgOperations} can't be retrieved with this method and * will throw an {@link IllegalArgumentException}</b>, use * {@link #createAttribute(ProtocolInfoAttributeName, String, Protocol)} * instead. * <p> * This method can only retrieve or create values in their * {@link String} form. Use the factory methods of the individual * classes to retrieve or create instances for values of other types. * * @param attributeName the {@link ProtocolInfoAttributeName}. * @param attributeValue the attribute {@link String} value. * @return The existing or created instance. * @throws ParseException If {@code attributeValue} can't be parsed. * @throws IllegalArgumentException If {@code attributeName} is * {@link DLNAOrgOperations#NAME}. */ public ProtocolInfoAttribute createAttribute( ProtocolInfoAttributeName attributeName, String attributeValue ) throws ParseException { if (DLNAOrgOperations.NAME.equals(attributeName)) { throw new IllegalArgumentException( "Cannot create DLNA.ORG_FLAGS instance because protocol information is needed " + "- use an overloaded version of this method which takes a protocol argument"); } return createAttribute(attributeName, attributeValue, null); } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, a new will * be created. * <p> * This method can only retrieve or create values in their {@link String} form. * Use the factory methods of the individual classes to retrieve or create * instances for values of other types. * * @param attributeName the attribute name {@link String} value. * @param attributeValue the attribute {@link String} value. * @param protocol the {@link Protocol} of the {@code protocolInfo} this * {@link ProtocolInfoAttribute} belongs to. This parameter * is only used when retrieving {@link DLNAOrgOperations} * instances and can be {@code null} for other attribute * types. * @return The existing or created instance. * @throws ParseException If {@code attributeValue} can't be parsed. */ public ProtocolInfoAttribute createAttribute( String attributeName, String attributeValue, Protocol protocol ) throws ParseException { return createAttribute( ProtocolInfoAttributeName.FACTORY.createAttributeName(attributeName), attributeValue, protocol ); } /** * Retrieves a predefined or cached {@link ProtocolInfoAttribute} * instance if it exists. If no matching instance is found, a new will * be created. * <p> * This method can only retrieve or create values in their {@link String} form. * Use the factory methods of the individual classes to retrieve or create * instances for values of other types. * * @param attributeName the {@link ProtocolInfoAttributeName}. * @param attributeValue the attribute {@link String} value. * @param protocol the {@link Protocol} of the {@code protocolInfo} this * {@link ProtocolInfoAttribute} belongs to. This parameter * is only used when retrieving {@link DLNAOrgOperations} * instances and can be {@code null} for other attribute * types. * @return The existing or created instance. * @throws ParseException If {@code attributeValue} can't be parsed. */ public ProtocolInfoAttribute createAttribute( ProtocolInfoAttributeName attributeName, String attributeValue, Protocol protocol ) throws ParseException { if (attributeName == null) { return null; } /* Check predefined and cached types, the following classes only has predefined instances: * * - DLNAOrgConversionIndicator * - DLNAOrgOperations * */ ProtocolInfoAttribute instance = getAttribute(attributeName, attributeValue, protocol); if (instance != null) { return instance; } if (DLNAOrgPlaySpeeds.NAME.equals(attributeName)) { return DLNAOrgPlaySpeeds.FACTORY.createPlaySpeeds(attributeValue); } else if (DLNAOrgFlags.NAME.equals(attributeName)) { DLNAOrgFlags flags = new DLNAOrgFlags(attributeValue); if (DLNAOrgFlags.IMPLIED.equals(flags)) { return DLNAOrgFlags.IMPLIED; } return flags; } else if (attributeName == DLNAOrgProfileName.NAME) { return DLNAOrgProfileName.FACTORY.createProfileName(attributeValue); } else if (PanasonicComProfileName.NAME.equals(attributeName)) { return PanasonicComProfileName.FACTORY.createProfileName(attributeValue); } else if (AribOrJpProfileName.NAME.equals(attributeName)) { return AribOrJpProfileName.FACTORY.createProfileName(attributeValue); } else if (attributeName.getName().contains("_PN")) { return DefaultGenericProfileName.FACTORY.createProfileName(attributeName, attributeValue); } // Create a new "generic" instance LOGGER.trace("Creating unknown ProtocolInfoAttribute \"{}\" with value \"{}\"", attributeName, attributeValue); return new StringAttribute(attributeName, attributeValue); } } /** * The most basic {@link ProtocolInfoAttribute} implementation where the * value is simply a {@link String}. Immutable. */ public static class StringAttribute implements ProtocolInfoAttribute { private static final long serialVersionUID = 1L; /** The attribute name */ protected final ProtocolInfoAttributeName name; /** The {@link String} value */ protected final String value; /** * Creates a new instance with the given values. * * @param name the attribute name. * @param value the attribute value. */ public StringAttribute(ProtocolInfoAttributeName name, String value) { this.name = name; this.value = value; } @Override public ProtocolInfoAttributeName getName() { return name; } @Override public String getNameString() { return name == null ? null : name.getName(); } @Override public String getValue() { return value; } @Override public String toString() { return name + " = " + value; } @Override public String getAttributeString() { return name == null || isBlank(name.getName()) || isBlank(value) ? "" : name.getName() + "=" + value; } } }