package cucumber.runtime.model;

import cucumber.runtime.CucumberException;
import cucumber.runtime.io.Resource;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FeatureBuilder {
    private static final Charset UTF8 = Charset.forName("UTF-8");
    private final List<CucumberFeature> cucumberFeatures = new ArrayList<>();
    private final MessageDigest md5;
    private final Set<String> pathsByChecksum = new HashSet<>();

    public FeatureBuilder() {
        try {
            this.md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new CucumberException(e);
        }
    }

    public List<CucumberFeature> build() {
        Collections.sort(cucumberFeatures, new CucumberFeature.CucumberFeatureUriComparator());
        return cucumberFeatures;
    }

    public void parse(Resource resource) {
        CucumberFeature feature = FeatureParser.parseResource(resource);

        String checksum = checksum(feature.getSource());
        if (pathsByChecksum.contains(checksum)) {
            return;
        }
        pathsByChecksum.add(checksum);
        cucumberFeatures.add(feature);

    }

    private String checksum(String gherkin) {
        return new BigInteger(1, md5.digest(gherkin.getBytes(UTF8))).toString(16);
    }


}

@@ -50,33 +50,27 @@
 runtime.
-CucumberException
+io.Resource
 ;%0Aimport
@@ -62,32 +62,35 @@
 esource;%0Aimport 
+io.
 cucumber.runtime
@@ -86,174 +86,76 @@
 ber.
-runtime.io.Resource;%0A%0Aimport java.math.BigInteger;%0Aimport java.nio.charset.Charset;%0Aimport java.security.MessageDigest;%0Aimport java.security.NoSuchAlgorithmException;
+core.logging.Logger;%0Aimport io.cucumber.core.logging.LoggerFactory;%0A
 %0Aimp
@@ -230,19 +230,19 @@
 til.Hash
-Set
+Map
 ;%0Aimport
@@ -275,19 +275,19 @@
 va.util.
-Set
+Map
 ;%0A%0Apubli
@@ -311,16 +311,17 @@
 ilder %7B%0A
+%0A
     priv
@@ -328,59 +328,71 @@
 ate 
-static final Charset UTF8 = Charset.forName(%22UTF-8%22
+final Logger log = LoggerFactory.getLogger(FeatureBuilder.class
 );%0A 
@@ -408,21 +408,28 @@
 e final 
-List%3C
+Map%3CString, 
 Cucumber
@@ -441,174 +441,82 @@
 re%3E 
-cucumber
+sourceTo
 Feature
-s
  = new 
-ArrayList
+HashMap
 %3C%3E();%0A
+%0A
     p
-rivate final MessageDigest md5;%0A    private final Set%3CString%3E pathsByChecksum = new HashSet%3C%3E();%0A%0A    public 
+ublic List%3CCucumber
 Feature
-B
+%3E b
 uild
-er
 () %7B
@@ -528,221 +528,91 @@
     
-try %7B%0A            this.md5 = MessageDigest.getInstance(%22MD5%22);%0A        %7D catch (NoSuchAlgorithmException e) %7B%0A            throw new CucumberException(e);%0A        %7D%0A    %7D%0A%0A    public List%3CCucumberFeature%3E build() %7B
+List%3CCucumberFeature%3E cucumberFeatures = new ArrayList%3C%3E(sourceToFeature.values());
 %0A   
@@ -811,17 +811,23 @@
 Feature 
-f
+parsedF
 eature =
@@ -866,25 +866,24 @@
 ource);%0A
-%0A
         
 String c
@@ -878,349 +878,355 @@
     
-String checksum = checksum(feature.getSource());%0A        if (pathsByChecksum.contains(checksum)) %7B%0A            return;%0A        %7D%0A        pathsByChecksum.add(checksum);%0A        cucumberFeatures.add(feature
+CucumberFeature existingFeature = sourceToFeature.get(parsedFeature.getSource());%0A        if (existingFeature != null) %7B%0A            log.warn(%22Duplicate feature ignored. %22 + parsedFeature.getUri() + %22 was identical to %22 + existingFeature.getUri()
 );%0A
-%0A
     
-%7D%0A%0A    private String checksum(String gherkin) %7B%0A        return new BigInteger(1, md5.digest(gherkin.getBytes(UTF8))).toString(16
+        return;%0A        %7D%0A        sourceToFeature.put(parsedFeature.getSource(), parsedFeature
 );%0A 
@@ -1230,12 +1230,10 @@
 ;%0A    %7D%0A
-%0A%0A
 %7D%0A

Simplify duplicate feature detection