/* * Copyright 2015 MiLaboratory.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.milaboratory.core.mutations; import com.milaboratory.core.sequence.Alphabet; import com.milaboratory.core.sequence.Sequence; import com.milaboratory.util.ArraysUtils; import java.util.Arrays; import static com.milaboratory.core.mutations.Mutation.*; public final class MutationsBuilder<S extends Sequence<S>> { private final Alphabet<S> alphabet; private final boolean reversed; private int[] mutations = null; private int size = 0; public MutationsBuilder(Alphabet<S> alphabet, boolean reversed, int[] mutations, int size) { this.alphabet = alphabet; this.reversed = reversed; this.mutations = mutations; this.size = size; } public MutationsBuilder(Alphabet<S> alphabet) { this(alphabet, false); } public MutationsBuilder(Alphabet<S> alphabet, boolean reversed) { this.alphabet = alphabet; this.reversed = reversed; } public int get(int index) { return mutations[index]; } public int size() { return size; } public void set(int index, int mutation) { mutations[index] = mutation; } public int getLast() { return mutations[size - 1]; } public void removeLast() { --size; } private void ensureInternalCapacity(int newSize) { if (size == -1) throw new IllegalStateException("Destroyed."); if (newSize == 0) { assert mutations == null; return; } if (mutations == null) mutations = new int[Math.max(newSize, 10)]; if (mutations.length < newSize) mutations = Arrays.copyOf(mutations, Math.max(newSize, 3 * mutations.length / 2 + 1)); } public MutationsBuilder<S> ensureCapacity(int capacity) { if (size == -1) throw new IllegalStateException("Destroyed."); if (capacity > 0) { if (mutations == null) mutations = new int[capacity]; else if (capacity > mutations.length) mutations = Arrays.copyOf(mutations, capacity); } return this; } public Mutations<S> createAndDestroy() { final int[] m; if (mutations == null) m = new int[0]; else if (mutations.length == size) m = mutations; else m = Arrays.copyOf(mutations, size); mutations = null; size = -1; if (reversed) ArraysUtils.reverse(m); if (m.length > 1) for (int i = 1; i < m.length; ++i) if (getPosition(m[i - 1]) > getPosition(m[i])) throw new IllegalArgumentException("Mutations must be appended in descending/ascending order (position) " + "depending on the value of reverse flag. Problem " + Mutation.encode(m[i - 1], alphabet) + ":" + Mutation.encode(m[i], alphabet) + " in " + MutationsUtil.encode(m, alphabet) + "."); return new Mutations<>(alphabet, m, true); } public MutationsBuilder<S> append(Mutations<S> other) { append(other, 0, other.size()); return this; } public MutationsBuilder<S> append(MutationsBuilder<S> other) { if (other.size == 0) return this; ensureInternalCapacity(size + other.size); System.arraycopy(other.mutations, 0, mutations, size, other.size); size += other.size; return this; } public MutationsBuilder<S> append(Mutations<S> other, int otherFrom, int length) { ensureInternalCapacity(size + length); if (length != 0) System.arraycopy(other.mutations, otherFrom, mutations, size, length); size += length; return this; } public MutationsBuilder<S> append(int[] other) { ensureInternalCapacity(size + other.length); for (int mutation : other) mutations[size++] = mutation; return this; } public MutationsBuilder<S> append(int mutation) { ensureInternalCapacity(size + 1); mutations[size++] = mutation; return this; } public MutationsBuilder<S> appendSubstitution(int position, int from, int to) { return append(createSubstitution(position, from, to)); } public MutationsBuilder<S> appendDeletion(int position, int from) { return append(createDeletion(position, from)); } public MutationsBuilder<S> appendInsertion(int position, int to) { return append(createInsertion(position, to)); } public MutationsBuilder<S> appendInsertion(int position, S insert) { ensureCapacity(size + insert.size()); for (int i = 0; i < insert.size(); i++) append(createInsertion(position, insert.codeAt(i))); return this; } public MutationsBuilder<S> appendDeletion(int position, int length, S originalSequence) { ensureCapacity(size + length); for (int i = 0; i < length; i++) append(createDeletion(position + i, originalSequence.codeAt(position + i))); return this; } public MutationsBuilder<S> reverseRange(int from, int to) { ArraysUtils.reverse(mutations, from, to); return this; } public MutationsBuilder<S> clone() { return new MutationsBuilder<>(alphabet, reversed, mutations == null ? null : mutations.clone(), size); } @Override public String toString() { return this.clone().createAndDestroy().toString(); } }