| /*=============================================================================# |
| # Copyright (c) 2009, 2018 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.data.impl; |
| |
| import java.io.Externalizable; |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| import java.util.Arrays; |
| |
| import org.eclipse.statet.rj.data.RCharacterStore; |
| import org.eclipse.statet.rj.data.RJIO; |
| import org.eclipse.statet.rj.data.RStore; |
| |
| |
| /** |
| * This implementation is limited to length of 2<sup>31</sup>-1. |
| */ |
| public class RFactor32Store extends AbstractFactorStore |
| implements RDataResizeExtension<Integer>, ExternalizableRStore, Externalizable { |
| |
| |
| private int length; |
| |
| protected int[] codes; |
| |
| protected RCharacter32Store codeLabels; |
| |
| |
| public RFactor32Store(final int length, final boolean isOrdered, final String[] levelLabels) { |
| if (levelLabels == null) { |
| throw new NullPointerException(); |
| } |
| this.length= length; |
| this.isOrdered= isOrdered; |
| this.codes= new int[length]; |
| Arrays.fill(this.codes, NA_integer_INT); |
| this.codeLabels= new RUniqueCharacter32Store(levelLabels); |
| } |
| |
| public RFactor32Store(final int[] codes, final boolean isOrdered, final String[] levelLabels) { |
| if (codes == null || levelLabels == null) { |
| throw new NullPointerException(); |
| } |
| this.length= codes.length; |
| this.isOrdered= isOrdered; |
| this.codes= codes; |
| this.codeLabels= new RUniqueCharacter32Store(levelLabels); |
| } |
| |
| |
| public RFactor32Store(final RJIO io, final int length) throws IOException { |
| this.length= length; |
| this.isOrdered= io.readBoolean(); |
| this.codes= io.readIntData(new int[length], length); |
| this.codeLabels= readLabels(io, io.readInt()); |
| } |
| protected RCharacter32Store readLabels(final RJIO io, final int l) throws IOException { |
| return new RUniqueCharacter32Store(io, l); |
| } |
| |
| @Override |
| public void writeExternal(final RJIO io) throws IOException { |
| io.writeBoolean(this.isOrdered); |
| io.writeIntData(this.codes, this.length); |
| io.writeInt(this.codeLabels.length()); |
| this.codeLabels.writeExternal(io); |
| } |
| |
| @Override |
| public void readExternal(final ObjectInput in) throws IOException { |
| this.isOrdered= in.readBoolean(); |
| this.length= in.readInt(); |
| this.codes= new int[this.length]; |
| for (int i= 0; i < this.length; i++) { |
| this.codes[i]= in.readInt(); |
| } |
| this.codeLabels= new RUniqueCharacter32Store(); |
| this.codeLabels.readExternal(in); |
| } |
| |
| @Override |
| public void writeExternal(final ObjectOutput out) throws IOException { |
| out.writeBoolean(this.isOrdered); |
| out.writeInt(this.length); |
| for (int i= 0; i < this.length; i++) { |
| out.writeInt(this.codes[i]); |
| } |
| this.codeLabels.writeExternal(out); |
| } |
| |
| |
| @Override |
| protected final boolean isStructOnly() { |
| return false; |
| } |
| |
| |
| protected final int length() { |
| return this.length; |
| } |
| |
| @Override |
| public final long getLength() { |
| return this.length; |
| } |
| |
| @Override |
| public boolean isNA(final int idx) { |
| return (this.codes[idx] <= 0); |
| } |
| |
| @Override |
| public boolean isNA(final long idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| return (this.codes[(int) idx] <= 0); |
| } |
| |
| @Override |
| public void setNA(final int idx) { |
| this.codes[idx]= NA_integer_INT; |
| } |
| |
| @Override |
| public void setNA(final long idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| this.codes[(int) idx]= NA_integer_INT; |
| } |
| |
| @Override |
| public boolean isMissing(final int idx) { |
| return (this.codes[idx] <= 0); |
| } |
| |
| @Override |
| public boolean isMissing(final long idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| return (this.codes[(int) idx] <= 0); |
| } |
| |
| @Override |
| public int getInt(final int idx) { |
| return this.codes[idx]; |
| } |
| |
| @Override |
| public int getInt(final long idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| return this.codes[(int) idx]; |
| } |
| |
| @Override |
| public void setInt(final int idx, final int code) { |
| if (code <= 0 || code > this.codeLabels.length()) { |
| throw new IllegalArgumentException(); |
| } |
| this.codes[idx]= code; |
| } |
| |
| @Override |
| public void setInt(final long idx, final int code) { |
| if (code <= 0 || code > this.codeLabels.length()) { |
| throw new IllegalArgumentException(); |
| } |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| this.codes[(int) idx]= code; |
| } |
| |
| @Override |
| public String getChar(final int idx) { |
| final int code= this.codes[idx]; |
| return (code > 0) ? this.codeLabels.getChar(code - 1): null; |
| } |
| |
| @Override |
| public String getChar(final long idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| final int code= this.codes[(int) idx]; |
| return (code > 0) ? this.codeLabels.getChar(code - 1): null; |
| } |
| |
| @Override |
| public void setChar(final int idx, final String data) { |
| final int code= this.codeLabels.indexOf(data, 0) + 1; |
| if (code <= 0) { |
| throw new IllegalArgumentException(); |
| } |
| this.codes[idx]= code; |
| } |
| |
| @Override |
| public void setChar(final long idx, final String data) { |
| final int code= this.codeLabels.indexOf(data, 0) + 1; |
| if (code <= 0) { |
| throw new IllegalArgumentException(); |
| } |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| this.codes[(int) idx]= code; |
| } |
| |
| |
| private void prepareInsert(final int[] idxs) { |
| this.codes= prepareInsert(this.codes, this.length, idxs); |
| this.length+= idxs.length; |
| } |
| |
| public void insertChar(final int idx, final String data) { |
| final int code= this.codeLabels.indexOf(data, 0) + 1; |
| if (code <= 0) { |
| throw new IllegalArgumentException(); |
| } |
| prepareInsert(new int[] { idx }); |
| this.codes[idx]= code; |
| } |
| |
| @Override |
| public void insertNA(final int idx) { |
| prepareInsert(new int[] { idx }); |
| this.codes[idx]= NA_integer_INT; |
| } |
| |
| @Override |
| public void insertNA(final int[] idxs) { |
| prepareInsert(idxs); |
| for (int idx= 0; idx < idxs.length; idx++) { |
| this.codes[idx]= NA_integer_INT; |
| } |
| } |
| |
| @Override |
| public void remove(final int idx) { |
| this.codes= remove(this.codes, this.length, new int[] { idx }); |
| this.length--; |
| } |
| |
| @Override |
| public void remove(final int[] idxs) { |
| this.codes= remove(this.codes, this.length, idxs); |
| this.length-= idxs.length; |
| } |
| |
| |
| @Override |
| public RCharacterStore getLevels() { |
| return this.codeLabels; |
| } |
| |
| @Override |
| public int getLevelCount() { |
| return this.codeLabels.length(); |
| } |
| |
| public void addLevel(final String label) { |
| insertLevel(this.codes.length, label); |
| } |
| |
| public void insertLevel(final int position, final String label) { |
| this.codeLabels.insertChar(position, label); |
| if (position < this.codeLabels.getLength()-1) { |
| final int length= length(); |
| for (int i= 0; i < length; i++) { |
| if (this.codes[i] >= position) { |
| this.codes[i]++; |
| } |
| } |
| } |
| } |
| |
| public void renameLevel(final String oldLabel, final String newLabel) { |
| final int code= this.codeLabels.indexOf(oldLabel, 0) + 1; |
| if (code <= 0) { |
| throw new IllegalArgumentException(); |
| } |
| this.codeLabels.setChar(code - 1, newLabel); |
| } |
| |
| public void removeLevel(final String label) { |
| final int code= this.codeLabels.indexOf(label, 0) + 1; |
| if (code <= 0) { |
| throw new IllegalArgumentException(); |
| } |
| this.codeLabels.remove(code - 1); |
| final int length= length(); |
| for (int i= 0; i < length; i++) { |
| if (this.codes[i] == code) { |
| this.codes[i]= NA_integer_INT; |
| } |
| else if (this.codes[i] > code) { |
| this.codes[i]--; |
| } |
| } |
| } |
| |
| @Override |
| public RCharacterStore toCharacterData() { |
| final String[] data= new String[length()]; |
| final int[] ints= this.codes; |
| for (int i= 0; i < data.length; i++) { |
| final int code= ints[i]; |
| if (code > 0) { |
| data[i]= this.codeLabels.getChar(this.codes[i] - 1); |
| } |
| } |
| return new RCharacter32Store(data); |
| } |
| |
| |
| @Override |
| public Integer get(final int idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| final int code= this.codes[idx]; |
| return (code > 0) ? |
| Integer.valueOf(code) : |
| null; |
| } |
| |
| @Override |
| public Integer get(final long idx) { |
| if (idx < 0 || idx >= length()) { |
| throw new IndexOutOfBoundsException(Long.toString(idx)); |
| } |
| final int code= this.codes[(int) idx]; |
| return (code > 0) ? |
| Integer.valueOf(code) : |
| null; |
| } |
| |
| @Override |
| public Integer[] toArray() { |
| final Integer[] array= new Integer[length()]; |
| final int[] ints= this.codes; |
| for (int i= 0; i < array.length; i++) { |
| final int code= ints[i]; |
| if (code > 0) { |
| array[i]= Integer.valueOf(code); |
| } |
| } |
| return array; |
| } |
| |
| |
| @Override |
| public long indexOfNA(long fromIdx) { |
| if (fromIdx >= Integer.MAX_VALUE) { |
| return -1; |
| } |
| if (fromIdx < 0) { |
| fromIdx= 0; |
| } |
| final int l= length(); |
| final int[] ints= this.codes; |
| for (int i= (int) fromIdx; i < l; i++) { |
| if (ints[i] == NA_integer_INT) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public long indexOf(final int code, long fromIdx) { |
| if (fromIdx >= Integer.MAX_VALUE |
| || code <= 0 || code > this.codeLabels.length()) { |
| return -1; |
| } |
| if (fromIdx < 0) { |
| fromIdx= 0; |
| } |
| final int l= length(); |
| final int[] ints= this.codes; |
| for (int i= (int) fromIdx; i < l; i++) { |
| if (ints[i] == code) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public long indexOf(final String character, final long fromIdx) { |
| final int code= ((character != null) ? |
| this.codeLabels.indexOf(character, 0) : |
| this.codeLabels.indexOfNA(0) ) + 1; |
| return indexOf(code, fromIdx); |
| } |
| |
| |
| @Override |
| public boolean allEqual(final RStore<?> other) { |
| throw new UnsupportedOperationException("Not yet implemented"); |
| } |
| |
| } |