Source:
<code>private void process(final DuplicateSet set) { if (nextSetsIterator.hasNext()) { throw new PicardException("nextSetsIterator is expected to be empty, but already contains data."); } final UmiGraph umiGraph = new UmiGraph(set, umiTag, molecularIdentifierTag, allowMissingUmis, duplexUmi); final String library = set.getRepresentative().getReadGroup().getLibrary(); final UmiMetrics metrics = umiMetricsMap.computeIfAbsent(library, UmiMetrics::new); final List<DuplicateSet> duplicateSets = umiGraph.joinUmisIntoDuplicateSets(maxEditDistanceToJoin); for (final DuplicateSet ds : duplicateSets) { final List<SAMRecord> records = ds.getRecords(); for (final SAMRecord rec : records) { final String currentUmi = UmiUtil.getTopStrandNormalizedDuplexUMI(rec, umiTag, duplexUmi); if (currentUmi != null) { if (currentUmi.contains("N")) { metrics.addUmiObservationN(); } else { if (!haveWeSeenFirstRead) { <START> metrics.MEAN_UMI_LENGTH = currentUmi.length() - StringUtils.countMatches(currentUmi, "-"); <END> haveWeSeenFirstRead = true; } else { if (metrics.MEAN_UMI_LENGTH != currentUmi.length() - StringUtils.countMatches(currentUmi, "-")) { throw new PicardException("UMIs of differing lengths were found."); } } final String inferredUmi = (String) rec.getTransientAttribute("inferredUmi"); metrics.OBSERVED_BASE_ERRORS += hammingDistance(currentUmi, inferredUmi); observedUmiBases += currentUmi.length() - StringUtils.countMatches(currentUmi, "-"); metrics.addUmiObservation(currentUmi, inferredUmi); } } } } metrics.DUPLICATE_SETS_WITH_UMI += duplicateSets.size(); metrics.DUPLICATE_SETS_IGNORING_UMI++; nextSetsIterator = duplicateSets.iterator(); }</code><technical_language>refactor this add a function UmiUtils</technical_language>

Target:
private void process(final DuplicateSet set) { if (nextSetsIterator.hasNext()) { throw new PicardException("nextSetsIterator is expected to be empty, but already contains data."); } final UmiGraph umiGraph = new UmiGraph(set, umiTag, molecularIdentifierTag, allowMissingUmis, duplexUmi); final String library = set.getRepresentative().getReadGroup().getLibrary(); final UmiMetrics metrics = umiMetricsMap.computeIfAbsent(library, UmiMetrics::new); final List<DuplicateSet> duplicateSets = umiGraph.joinUmisIntoDuplicateSets(maxEditDistanceToJoin); for (final DuplicateSet ds : duplicateSets) { final List<SAMRecord> records = ds.getRecords(); for (final SAMRecord rec : records) { final String currentUmi = UmiUtil.getTopStrandNormalizedUmi(rec, umiTag, duplexUmi); if (currentUmi != null) { if (currentUmi.contains("N")) { metrics.addUmiObservationN(); } else { final int umiLength = UmiUtil.getUmiLength(currentUmi); if (!haveWeSeenFirstRead) { metrics.MEAN_UMI_LENGTH = umiLength; haveWeSeenFirstRead = true; } else { if (metrics.MEAN_UMI_LENGTH != umiLength) { throw new PicardException("UMIs of differing lengths were found."); } } final String inferredUmi = (String) rec.getTransientAttribute(UmiUtil.INFERRED_UMI_TRANSIENT_TAG); metrics.OBSERVED_BASE_ERRORS += StringUtil.hammingDistance(currentUmi, inferredUmi); observedUmiBases += umiLength; metrics.addUmiObservation(currentUmi, inferredUmi); } } } } metrics.DUPLICATE_SETS_WITH_UMI += duplicateSets.size(); metrics.DUPLICATE_SETS_IGNORING_UMI++; nextSetsIterator = duplicateSets.iterator(); }