001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.generic;
019
020/** 
021 * SWITCH - Branch depending on int value, generates either LOOKUPSWITCH or
022 * TABLESWITCH instruction, depending on whether the match values (int[]) can be
023 * sorted with no gaps between the numbers.
024 *
025 * @version $Id: SWITCH.java 1749603 2016-06-21 20:50:19Z ggregory $
026 */
027public final class SWITCH implements CompoundInstruction {
028
029    private int[] match;
030    private InstructionHandle[] targets;
031    private Select instruction;
032    private int match_length;
033
034
035    /**
036     * Template for switch() constructs. If the match array can be
037     * sorted in ascending order with gaps no larger than max_gap
038     * between the numbers, a TABLESWITCH instruction is generated, and
039     * a LOOKUPSWITCH otherwise. The former may be more efficient, but
040     * needs more space.
041     * 
042     * Note, that the key array always will be sorted, though we leave
043     * the original arrays unaltered.
044     *
045     * @param match array of match values (case 2: ... case 7: ..., etc.)
046     * @param targets the instructions to be branched to for each case
047     * @param target the default target
048     * @param max_gap maximum gap that may between case branches
049     */
050    public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target, final int max_gap) {
051        this.match = match.clone();
052        this.targets = targets.clone();
053        if ((match_length = match.length) < 2) {
054            instruction = new TABLESWITCH(match, targets, target);
055        } else {
056            sort(0, match_length - 1);
057            if (matchIsOrdered(max_gap)) {
058                fillup(max_gap, target);
059                instruction = new TABLESWITCH(this.match, this.targets, target);
060            } else {
061                instruction = new LOOKUPSWITCH(this.match, this.targets, target);
062            }
063        }
064    }
065
066
067    public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target) {
068        this(match, targets, target, 1);
069    }
070
071
072    private void fillup( final int max_gap, final InstructionHandle target ) {
073        final int max_size = match_length + match_length * max_gap;
074        final int[] m_vec = new int[max_size];
075        final InstructionHandle[] t_vec = new InstructionHandle[max_size];
076        int count = 1;
077        m_vec[0] = match[0];
078        t_vec[0] = targets[0];
079        for (int i = 1; i < match_length; i++) {
080            final int prev = match[i - 1];
081            final int gap = match[i] - prev;
082            for (int j = 1; j < gap; j++) {
083                m_vec[count] = prev + j;
084                t_vec[count] = target;
085                count++;
086            }
087            m_vec[count] = match[i];
088            t_vec[count] = targets[i];
089            count++;
090        }
091        match = new int[count];
092        targets = new InstructionHandle[count];
093        System.arraycopy(m_vec, 0, match, 0, count);
094        System.arraycopy(t_vec, 0, targets, 0, count);
095    }
096
097
098    /**
099     * Sort match and targets array with QuickSort.
100     */
101    private void sort( final int l, final int r ) {
102        int i = l;
103        int j = r;
104        int h;
105        final int m = match[(l + r) / 2];
106        InstructionHandle h2;
107        do {
108            while (match[i] < m) {
109                i++;
110            }
111            while (m < match[j]) {
112                j--;
113            }
114            if (i <= j) {
115                h = match[i];
116                match[i] = match[j];
117                match[j] = h; // Swap elements
118                h2 = targets[i];
119                targets[i] = targets[j];
120                targets[j] = h2; // Swap instructions, too
121                i++;
122                j--;
123            }
124        } while (i <= j);
125        if (l < j) {
126            sort(l, j);
127        }
128        if (i < r) {
129            sort(i, r);
130        }
131    }
132
133
134    /**
135     * @return match is sorted in ascending order with no gap bigger than max_gap?
136     */
137    private boolean matchIsOrdered( final int max_gap ) {
138        for (int i = 1; i < match_length; i++) {
139            if (match[i] - match[i - 1] > max_gap) {
140                return false;
141            }
142        }
143        return true;
144    }
145
146
147    @Override
148    public final InstructionList getInstructionList() {
149        return new InstructionList(instruction);
150    }
151
152
153    public final Instruction getInstruction() {
154        return instruction;
155    }
156}