// Path: src/main/java/comeon/model/Media.java
// public final class Media {
//     private final PropertyChangeSupport pcs;
// 
//     private final File file;
// 
//     private final String fileName;
// 
//     private final Template template;
// 
//     private String templateText;
// 
//     private String renderedTemplate;
// 
//     private final Map<String, Object> metadata;
// 
//     private final byte[] thumbnail;
// 
//     private State state;
// 
//     public Media(final File file, final String fileName, final Template template, final Map<String, Object> metadata,
//                  final byte[] thumbnail) {
//         super();
//         this.pcs = new PropertyChangeSupport(this);
//         this.file = file;
//         this.fileName = fileName;
//         this.template = template;
//         this.templateText = template.getTemplateText();
//         this.metadata = metadata;
//         this.thumbnail = thumbnail;
//         this.state = State.ToBeUploaded;
//     }
// 
//     public void renderTemplate(final User user) {
//         this.setRenderedTemplate(template.getKind().render(template, templateText, user, this));
//     }
// 
//     public File getFile() {
//         return file;
//     }
// 
//     public Template getTemplate() {
//         return template;
//     }
// 
//     public String getTemplateText() {
//         return templateText;
//     }
// 
//     public void setTemplateText(final String templateText) {
//         final String oldTemplateText = this.templateText;
//         this.templateText = templateText;
//         pcs.firePropertyChange("templateText", oldTemplateText, templateText);
//     }
// 
//     public String getRenderedTemplate() {
//         return renderedTemplate;
//     }
// 
//     public void setRenderedTemplate(final String renderedTemplate) {
//         final String oldRenderedTemplate = this.renderedTemplate;
//         this.renderedTemplate = renderedTemplate;
//         pcs.firePropertyChange("renderedTemplate", oldRenderedTemplate, renderedTemplate);
//     }
// 
//     public Map<String, Object> getMetadata() {
//         return metadata;
//     }
// 
//     public byte[] getThumbnail() {
//         return thumbnail;
//     }
// 
//     public String getFileName() {
//         return fileName;
//     }
// 
//     public State getState() {
//         return state;
//     }
// 
//     public void setState(final State state) {
//         final State oldState = this.state;
//         this.state = state;
//         pcs.firePropertyChange("state", oldState, state);
//     }
// 
//     public void addPropertyChangeListener(final PropertyChangeListener pcl) {
//         this.pcs.addPropertyChangeListener(pcl);
//     }
// 
//     public void removePropertyChangeLister(final PropertyChangeListener pcl) {
//         this.pcs.removePropertyChangeListener(pcl);
//     }
// 
//     @Override
//     public boolean equals(final Object obj) {
//         final boolean isEqual;
//         if (obj == null) {
//             isEqual = false;
//         } else if (obj instanceof Media) {
//             final Media o = (Media) obj;
//             isEqual = this.file.equals(o.file);
//         } else {
//             isEqual = false;
//         }
//         return isEqual;
//     }
// 
//     @Override
//     public int hashCode() {
//         return this.file.hashCode();
//     }
// 
//     public enum State {
//         ToBeUploaded,
//         UploadedSuccessfully,
//         FailedUpload
//     }
// }


import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.bean.CsvToBean;
import comeon.model.Media;
import org.apache.commons.beanutils.LazyDynaMap;
import org.apache.commons.beanutils.NestedNullException;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
        final CGLibMappingStrategy strategy = new CGLibMappingStrategy();
        final CsvToBean<Object> csvToBean = new CsvToBean<>();
        try (final CSVReader reader = new CSVReader(Files.newBufferedReader(metadataFile, charset),
                separator, quote, escape, skipLines, strictQuotes, ignoreLeadingWhiteSpace)) {
            return csvToBean.parse(strategy, reader);
        }
    }

    @Override
    public Object getMediaMetadata(final Media media, final Map<String, Object> mediaMetadata) {
        try {
            final LazyDynaMap bean = new LazyDynaMap(mediaMetadata);
            final String key = String.valueOf(PropertyUtils.getNestedProperty(bean, mediaExpression));
            return metadata.get(keyTransformer.transform(key));
        } catch (final IllegalAccessException | InvocationTargetException | NestedNullException | NoSuchMethodException e) {
            try {
                final String key = String.valueOf(PropertyUtils.getNestedProperty(media, mediaExpression));
                return metadata.get(keyTransformer.transform(key));
            } catch (final IllegalAccessException | InvocationTargetException | NoSuchMethodException e2) {
                LOGGER.warn("Can't get property {} from media", mediaExpression, e2);
                return null;
            }
        }
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("metadataFile", metadataFile)
                .append("mediaExpression", mediaExpression)
// Path: src/main/java/comeon/model/Media.java
// public final class Media {
//     private final PropertyChangeSupport pcs;
// 
//     private final File file;
// 
//     private final String fileName;
// 
//     private final Template template;
// 
//     private String templateText;
// 
//     private String renderedTemplate;
// 
//     private final Map<String, Object> metadata;
// 
//     private final byte[] thumbnail;
// 
//     private State state;
// 
//     public Media(final File file, final String fileName, final Template template, final Map<String, Object> metadata,
//                  final byte[] thumbnail) {
//         super();
//         this.pcs = new PropertyChangeSupport(this);
//         this.file = file;
//         this.fileName = fileName;
//         this.template = template;
//         this.templateText = template.getTemplateText();
//         this.metadata = metadata;
//         this.thumbnail = thumbnail;
//         this.state = State.ToBeUploaded;
//     }
// 
//     public void renderTemplate(final User user) {
//         this.setRenderedTemplate(template.getKind().render(template, templateText, user, this));
//     }
// 
//     public File getFile() {
//         return file;
//     }
// 
//     public Template getTemplate() {
//         return template;
//     }
// 
//     public String getTemplateText() {
//         return templateText;
//     }
// 
//     public void setTemplateText(final String templateText) {
//         final String oldTemplateText = this.templateText;
//         this.templateText = templateText;
//         pcs.firePropertyChange("templateText", oldTemplateText, templateText);
//     }
// 
//     public String getRenderedTemplate() {
//         return renderedTemplate;
//     }
// 
//     public void setRenderedTemplate(final String renderedTemplate) {
//         final String oldRenderedTemplate = this.renderedTemplate;
//         this.renderedTemplate = renderedTemplate;
//         pcs.firePropertyChange("renderedTemplate", oldRenderedTemplate, renderedTemplate);
//     }
// 
//     public Map<String, Object> getMetadata() {
//         return metadata;
//     }
// 
//     public byte[] getThumbnail() {
//         return thumbnail;
//     }
// 
//     public String getFileName() {
//         return fileName;
//     }
// 
//     public State getState() {
//         return state;
//     }
// 
//     public void setState(final State state) {
//         final State oldState = this.state;
//         this.state = state;
//         pcs.firePropertyChange("state", oldState, state);
//     }
// 
//     public void addPropertyChangeListener(final PropertyChangeListener pcl) {
//         this.pcs.addPropertyChangeListener(pcl);
//     }
// 
//     public void removePropertyChangeLister(final PropertyChangeListener pcl) {
//         this.pcs.removePropertyChangeListener(pcl);
//     }
// 
//     @Override
//     public boolean equals(final Object obj) {
//         final boolean isEqual;
//         if (obj == null) {
//             isEqual = false;
//         } else if (obj instanceof Media) {
//             final Media o = (Media) obj;
//             isEqual = this.file.equals(o.file);
//         } else {
//             isEqual = false;
//         }
//         return isEqual;
//     }
// 
//     @Override
//     public int hashCode() {
//         return this.file.hashCode();
//     }
// 
//     public enum State {
//         ToBeUploaded,
//         UploadedSuccessfully,
//         FailedUpload
//     }
// }


// Path: src/main/java/comeon/core/extmetadata/CsvMetadataSource.java
import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.bean.CsvToBean;
import comeon.model.Media;
import org.apache.commons.beanutils.LazyDynaMap;
import org.apache.commons.beanutils.NestedNullException;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

        final CGLibMappingStrategy strategy = new CGLibMappingStrategy();
        final CsvToBean<Object> csvToBean = new CsvToBean<>();
        try (final CSVReader reader = new CSVReader(Files.newBufferedReader(metadataFile, charset),
                separator, quote, escape, skipLines, strictQuotes, ignoreLeadingWhiteSpace)) {
            return csvToBean.parse(strategy, reader);
        }
    }

    @Override
    public Object getMediaMetadata(final Media media, final Map<String, Object> mediaMetadata) {
        try {
            final LazyDynaMap bean = new LazyDynaMap(mediaMetadata);
            final String key = String.valueOf(PropertyUtils.getNestedProperty(bean, mediaExpression));
            return metadata.get(keyTransformer.transform(key));
        } catch (final IllegalAccessException | InvocationTargetException | NestedNullException | NoSuchMethodException e) {
            try {
                final String key = String.valueOf(PropertyUtils.getNestedProperty(media, mediaExpression));
                return metadata.get(keyTransformer.transform(key));
            } catch (final IllegalAccessException | InvocationTargetException | NoSuchMethodException e2) {
                LOGGER.warn("Can't get property {} from media", mediaExpression, e2);
                return null;
            }
        }
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("metadataFile", metadataFile)
                .append("mediaExpression", mediaExpression)
                .append("metadataExpression", metadataExpression)