// Path: downloader/src/main/java/org/aisen/download/utils/Utils.java
// public class Utils {
// 
//     public static String generateMD5(Uri uri, Uri fileUri) {
//         String key = uri.toString() + fileUri.toString();
// 
//         try {
//             MessageDigest e = MessageDigest.getInstance("MD5");
//             e.update(key.getBytes());
//             byte[] bytes = e.digest();
//             StringBuilder sb = new StringBuilder();
// 
//             for(int i = 0; i < bytes.length; ++i) {
//                 String hex = Integer.toHexString(255 & bytes[i]);
//                 if(hex.length() == 1) {
//                     sb.append('0');
//                 }
// 
//                 sb.append(hex);
//             }
// 
//             return sb.toString();
//         } catch (NoSuchAlgorithmException var6) {
//             return String.valueOf(key.hashCode());
//         }
//     }
// 
//     public static ThreadPoolExecutor buildDownloadExecutor(int maxThread) {
//         final int maxConcurrent = maxThread;
// 
//         // Create a bounded thread pool for executing downloads; it creates
//         // threads as needed (up to maximum) and reclaims them when finished.
//         final ThreadPoolExecutor executor = new ThreadPoolExecutor(
//                 maxConcurrent, maxConcurrent, 10, TimeUnit.SECONDS,
//                 new LinkedBlockingQueue<Runnable>()) {
// 
//             @Override
//             protected void afterExecute(Runnable r, Throwable t) {
//                 super.afterExecute(r, t);
// 
//                 if (t == null && r instanceof Future<?>) {
//                     try {
//                         ((Future<?>) r).get();
//                     } catch (CancellationException ce) {
//                         t = ce;
//                     } catch (ExecutionException ee) {
//                         t = ee.getCause();
//                     } catch (InterruptedException ie) {
//                         Thread.currentThread().interrupt();
//                     }
//                 }
// 
//                 if (t != null) {
//                     DLogger.w("Utils", "Uncaught exception", t);
//                 }
//             }
//         };
//         executor.allowCoreThreadTimeOut(true);
//         return executor;
//     }
// 
// }


import org.aisen.download.utils.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
package org.aisen.download;



/**
 * Created by wangdan on 16/6/15.
 */
public class DownloadProxy implements IDownloadSubject {

    private final Map<String, List<IDownloadObserver>> observers;

    public DownloadProxy() {
        observers = new HashMap<>();
    }

    @Override
    final public void attach(IDownloadObserver observer) {
        if (observer == null || observer.downloadURI() == null || observer.downloadFileURI() == null)
            return;

// Path: downloader/src/main/java/org/aisen/download/utils/Utils.java
// public class Utils {
// 
//     public static String generateMD5(Uri uri, Uri fileUri) {
//         String key = uri.toString() + fileUri.toString();
// 
//         try {
//             MessageDigest e = MessageDigest.getInstance("MD5");
//             e.update(key.getBytes());
//             byte[] bytes = e.digest();
//             StringBuilder sb = new StringBuilder();
// 
//             for(int i = 0; i < bytes.length; ++i) {
//                 String hex = Integer.toHexString(255 & bytes[i]);
//                 if(hex.length() == 1) {
//                     sb.append('0');
//                 }
// 
//                 sb.append(hex);
//             }
// 
//             return sb.toString();
//         } catch (NoSuchAlgorithmException var6) {
//             return String.valueOf(key.hashCode());
//         }
//     }
// 
//     public static ThreadPoolExecutor buildDownloadExecutor(int maxThread) {
//         final int maxConcurrent = maxThread;
// 
//         // Create a bounded thread pool for executing downloads; it creates
//         // threads as needed (up to maximum) and reclaims them when finished.
//         final ThreadPoolExecutor executor = new ThreadPoolExecutor(
//                 maxConcurrent, maxConcurrent, 10, TimeUnit.SECONDS,
//                 new LinkedBlockingQueue<Runnable>()) {
// 
//             @Override
//             protected void afterExecute(Runnable r, Throwable t) {
//                 super.afterExecute(r, t);
// 
//                 if (t == null && r instanceof Future<?>) {
//                     try {
//                         ((Future<?>) r).get();
//                     } catch (CancellationException ce) {
//                         t = ce;
//                     } catch (ExecutionException ee) {
//                         t = ee.getCause();
//                     } catch (InterruptedException ie) {
//                         Thread.currentThread().interrupt();
//                     }
//                 }
// 
//                 if (t != null) {
//                     DLogger.w("Utils", "Uncaught exception", t);
//                 }
//             }
//         };
//         executor.allowCoreThreadTimeOut(true);
//         return executor;
//     }
// 
// }


// Path: downloader/src/main/java/org/aisen/download/DownloadProxy.java
import org.aisen.download.utils.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

package org.aisen.download;



/**
 * Created by wangdan on 16/6/15.
 */
public class DownloadProxy implements IDownloadSubject {

    private final Map<String, List<IDownloadObserver>> observers;

    public DownloadProxy() {
        observers = new HashMap<>();
    }

    @Override
    final public void attach(IDownloadObserver observer) {
        if (observer == null || observer.downloadURI() == null || observer.downloadFileURI() == null)
            return;

        String key = Utils.generateMD5(observer.downloadURI(), observer.downloadFileURI());