/** * Copyright 2011-2017 Asakusa Framework Team. * * 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.asakusafw.runtime.io.text.tabular; import java.util.Arrays; import java.util.List; final class CharMap { private static final int[] EMPTY_TABLE = new int[0]; static final int ABSENT = -1; static final int NULL_CHARACTER = Character.MAX_VALUE + 1; static final CharMap EMPTY = new CharMap(Character.MAX_VALUE + 1, EMPTY_TABLE, ABSENT); private final int baseIndex; private final int[] entries; private final int nullKey; private CharMap(int baseIndex, int[] entries, int nullKey) { this.baseIndex = baseIndex; this.entries = entries; this.nullKey = nullKey; } static CharMap forward(EscapeSequence sequence) { List<EscapeSequence.Entry> mappings = sequence.getEntries(); if (mappings.isEmpty()) { return EMPTY; } int baseIndex = mappings.stream().mapToInt(c -> c.from).min().getAsInt(); int size = mappings.stream().mapToInt(c -> c.from).max().getAsInt() - baseIndex + 1; int[] entries = new int[size]; Arrays.fill(entries, ABSENT); int nullChar = ABSENT; for (EscapeSequence.Entry entry : mappings) { int index = entry.from - baseIndex; if (entries[index] != ABSENT) { continue; } if (entry.to == null) { nullChar = entry.from; entries[index] = NULL_CHARACTER; } else { entries[index] = entry.to; } } return new CharMap(baseIndex, entries, nullChar); } static CharMap backward(EscapeSequence sequence) { List<EscapeSequence.Entry> mappings = sequence.getEntries(); if (mappings.isEmpty()) { return EMPTY; } if (mappings.stream().noneMatch(c -> c.to != null)) { // only null rule return new CharMap(Character.MAX_VALUE + 1, EMPTY_TABLE, mappings.stream() .filter(c -> c.to == null) .map(c -> c.from) .findFirst() .get()); } int baseIndex = mappings.stream().filter(c -> c.to != null).mapToInt(c -> c.to).min().getAsInt(); int size = mappings.stream().filter(c -> c.to != null).mapToInt(c -> c.to).max().getAsInt() - baseIndex + 1; int[] entries = new int[size]; Arrays.fill(entries, ABSENT); int nullChar = ABSENT; for (EscapeSequence.Entry entry : mappings) { if (entry.to == null) { if (nullChar != ABSENT) { continue; } nullChar = entry.from; } else { int index = entry.to - baseIndex; if (entries[index] != ABSENT) { continue; } entries[index] = entry.from; } } return new CharMap(baseIndex, entries, nullChar); } public int get(int c) { int begin = baseIndex; if (c < begin) { return ABSENT; } int index = c - begin; int[] es = entries; return index < es.length ? es[index] : ABSENT; } public int getNullKey() { return nullKey; } }