/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * 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.
 */


/*
 * Entry point and tests that are expected to succeed.
 */
public class Main {
    /**
     * Drives tests.
     */
    public static void main(String[] args) {
        Main m = new Main();

        m.recursiveSync(0);

        m.nestedMayThrow(false);
        try {
            m.nestedMayThrow(true);
            System.err.println("nestedThrow(true) did not throw");
        } catch (MyException me) {}
        System.out.println("nestedMayThrow ok");

        m.constantLock();
        System.out.println("constantLock ok");

        m.notExcessiveNesting();
        try {
            TooDeep.excessiveNesting();
            System.err.println("excessiveNesting did not throw");
        } catch (VerifyError ve) {}
        System.out.println("excessiveNesting ok");

        m.notNested();
        System.out.println("notNested ok");

        Object obj1 = new Object();
        Object obj2 = new Object();

        m.twoPath(obj1, obj2, 0);
        System.out.println("twoPath ok");

        m.triplet(obj1, obj2, 0);
        System.out.println("triplet ok");
    }

    /**
     * Recursive synchronized method.
     */
    synchronized void recursiveSync(int iter) {
        if (iter < 40) {
            recursiveSync(iter+1);
        } else {
            System.out.println("recursiveSync ok");
        }
    }

    /**
     * Tests simple nesting, with and without a throw.
     */
    void nestedMayThrow(boolean doThrow) {
        synchronized (this) {
            synchronized (Main.class) {
                synchronized (new Object()) {
                    synchronized(Class.class) {
                        if (doThrow) {
                            throw new MyException();
                        }
                    }
                }
            }
        }
    }

    /**
     * Exercises bug 3215458.
     */
    void constantLock() {
        Class thing = Thread.class;
        synchronized (Thread.class) {}
    }

    /**
     * Confirms that we can have 32 nested monitors on one method.
     */
    void notExcessiveNesting() {
        synchronized (this) {   // 1
        synchronized (this) {   // 2
        synchronized (this) {   // 3
        synchronized (this) {   // 4
        synchronized (this) {   // 5
        synchronized (this) {   // 6
        synchronized (this) {   // 7
        synchronized (this) {   // 8
        synchronized (this) {   // 9
        synchronized (this) {   // 10
        synchronized (this) {   // 11
        synchronized (this) {   // 12
        synchronized (this) {   // 13
        synchronized (this) {   // 14
        synchronized (this) {   // 15
        synchronized (this) {   // 16
        synchronized (this) {   // 17
        synchronized (this) {   // 18
        synchronized (this) {   // 19
        synchronized (this) {   // 20
        synchronized (this) {   // 21
        synchronized (this) {   // 22
        synchronized (this) {   // 23
        synchronized (this) {   // 24
        synchronized (this) {   // 25
        synchronized (this) {   // 26
        synchronized (this) {   // 27
        synchronized (this) {   // 28
        synchronized (this) {   // 29
        synchronized (this) {   // 30
        synchronized (this) {   // 31
        synchronized (this) {   // 32
        }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
    }

    /**
     * Confirms that we can have more than 32 non-nested monitors in one
     * method.
     */
    void notNested() {
        synchronized (this) {}  // 1
        synchronized (this) {}  // 2
        synchronized (this) {}  // 3
        synchronized (this) {}  // 4
        synchronized (this) {}  // 5
        synchronized (this) {}  // 6
        synchronized (this) {}  // 7
        synchronized (this) {}  // 8
        synchronized (this) {}  // 9
        synchronized (this) {}  // 10
        synchronized (this) {}  // 11
        synchronized (this) {}  // 12
        synchronized (this) {}  // 13
        synchronized (this) {}  // 14
        synchronized (this) {}  // 15
        synchronized (this) {}  // 16
        synchronized (this) {}  // 17
        synchronized (this) {}  // 18
        synchronized (this) {}  // 19
        synchronized (this) {}  // 20
        synchronized (this) {}  // 21
        synchronized (this) {}  // 22
        synchronized (this) {}  // 23
        synchronized (this) {}  // 24
        synchronized (this) {}  // 25
        synchronized (this) {}  // 26
        synchronized (this) {}  // 27
        synchronized (this) {}  // 28
        synchronized (this) {}  // 29
        synchronized (this) {}  // 30
        synchronized (this) {}  // 31
        synchronized (this) {}  // 32
        synchronized (this) {}  // 33
        synchronized (this) {}  // 34
    }

    /* does nothing but ensure that the compiler doesn't discard an object */
    private void doNothing(Object obj) {}

    /**
     * Conditionally uses one of the synchronized objects.
     */
    public void twoPath(Object obj1, Object obj2, int x) {
        Object localObj;

        synchronized (obj1) {
            synchronized(obj2) {
                if (x == 0) {
                    localObj = obj2;
                } else {
                    localObj = obj1;
                }
            }
        }

        doNothing(localObj);
    }

    /**
     * Lock the monitor two or three times, and make use of the locked or
     * unlocked object.
     */
    public void triplet(Object obj1, Object obj2, int x) {
        Object localObj;

        synchronized (obj1) {
            synchronized(obj1) {
                if (x == 0) {
                    synchronized(obj1) {
                        localObj = obj2;
                    }
                } else {
                    localObj = obj1;
                }
            }
        }

        doNothing(localObj);
    }
}