/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The contents of this file are subject to the terms of either the Universal Permissive License
 * v 1.0 as shown at http://oss.oracle.com/licenses/upl
 *
 * or the following license:
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with
 * the distribution.
 * 
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package examples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Vector;

/**
 * A simple piece of code that generates a bunch of problematic data structures and then goes into
 * sleep for long enough for the user to take a heap dump. The resulting dump is stored alongside
 * the test source files and is analyzed in org.openjdk.jmc.joverflow.stats.VariousIssuesTest2.
 * <p>
 * IMPORTANT: a heap dump for this class should be generated by the JVM running in 32-bit mode.
 * Numbers in the test are based on pointer size etc. in this mode, where we know their exact values
 * and don't have to care about different object header size on different VMs, compressed
 * references, etc.
 */
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
public class VariousIssues2 {

	public static final int EMPTY_VECTOR1_SIZE = 20000;
	public static final int EMPTY_VECTOR2_SIZE = 19000;
	public static final int EMPTY_VECTOR3_SIZE = 18000;
	public static final int EMPTY_VECTOR4_SIZE = 17000;
	public static final int EMPTY_VECTOR5_SIZE = 16000;
	public static final int EMPTY_VECTOR6_SIZE = 15000;

	// This chain head object points directly to bad data
	CustomRef1 chain1Head;

	// Points to bad data through one intermediate object
	CustomRef1 chain2Head;

	// Points to bad data through an ArrayList
	CustomRef1 chain3Head;

	// Points to bad data through LLElementC linked list
	CustomRef2 chain4Head;

	// Points to bad data through a linked list which is a mix
	// of LLElementP and LLElementC
	CustomRef2 chain5Head;

	// Points to bad data through a very long (several thousand
	// elements) linked list of LLElementP. Used to check that
	// we can handle such lists without StackOverflowError that
	// we previously could run into because of recursion.
	CustomRef2 chain6Head;

	public static final int INTEGER_ARRAY_SIZE = 1000;
	public static final int INTEGER_ARRAY_NONNULL_ELEMENTS = 900;
	public static final int SHORT_ARRAY_SIZE = 1000;
	public static final int SHORT_ARRAY_UNIQUE_NUMS = 10;
	public static final int CHARACTER_MAP_SIZE = 500;

	// In this array, all Integer elements are unique
	private Integer arrayOfUniqueIntegers[];
	// This array references only SHORT_ARRAY_UNIQUE_NUMS distinct objects
	private Short arrayOfShorts[];
	// In this map, all keys are unique Character elements
	private LinkedHashMap<Character, String> characterMap;

	public static final int NUM_OF_SMALL_SPARSE_HASHMAPS = 600;
	public static final int NUM_OF_LARGE_SPARSE_HASHMAPS = 400;
	public static final int NUM_OF_SMALL_SPARSE_ARRAYLISTS = 600;
	public static final int NUM_OF_LARGE_SPARSE_ARRAYLISTS = 400;
	public static final int LARGE_MAP_EXPLICIT_CAPACITY = 32;
	public static final int NUM_ELS_IN_MAP = 15;

	private Object[] smallSparseHashMaps, largeSparseHashMaps, smallSparseArrayLists, largeSparseArrayLists;

	// Note that for some reason, whatever I do, these objects don't get properly
	// saved in the heap dump. That is, references to them are saved, but there
	// seem to be no objects with these references in the heap dump.
	private String STR_CONST1 = "Foo";
	private String STR_CONST2 = "Bar";

	public static class CustomRef1 {
		Object ref;
	}

	public static class CustomRef2 {
		Object ref;
	}

	// "Parent" linked list class
	public static class LLElementP {
		Object ref;
		LLElementP next;
	}

	// Parent's "child" linked list class
	public static class LLElementC extends LLElementP {
	}

	/** Main method that initializes all data and goes into sleep */
	public static void main(String args[]) {
		VariousIssues2 v = new VariousIssues2();

		ExampleUtils.printPidAndSleep("various-issues2.hprof");
	}

	/** Made public to be used in the same test that analyzes the heap dump */
	public VariousIssues2() {
		// Construct various interesting reference chains
		constructRefChains();

		constructStructsWithBoxedNums();

		constructSparseCollections();
	}

	private void constructRefChains() {
		chain1Head = new CustomRef1();
		chain1Head.ref = new Vector(EMPTY_VECTOR1_SIZE);

		chain2Head = new CustomRef1();
		CustomRef2 customRef2 = new CustomRef2();
		chain2Head.ref = customRef2;
		customRef2.ref = new Vector(EMPTY_VECTOR2_SIZE);

		chain3Head = new CustomRef1();
		ArrayList arList = new ArrayList(1);
		chain3Head.ref = arList;
		arList.add(new Vector(EMPTY_VECTOR3_SIZE));

		chain4Head = new CustomRef2();
		CustomRef1 customRef1 = new CustomRef1();
		chain4Head.ref = customRef1;
		LLElementC head1 = new LLElementC();
		customRef1.ref = head1;
		LLElementC current1 = head1;
		for (int i = 0; i < 10; i++) {
			LLElementC next1 = new LLElementC();
			current1.next = next1;
			current1 = next1;
		}
		current1.ref = new Vector(EMPTY_VECTOR4_SIZE);

		// chain5Head -> CustomRef2.ref -> LLElementC.next -> LLElementP.next -> LLElementC.ref -> bad vector
		chain5Head = new CustomRef2();
		LLElementC head2 = new LLElementC();
		chain5Head.ref = head2;
		LLElementP next2 = new LLElementP();
		head2.next = next2;
		next2.next = new LLElementC();
		next2 = next2.next;
		next2.ref = new Vector(EMPTY_VECTOR5_SIZE);

		// chain6Head -> CustomRef2.ref -> {LLElementP.next} -> LLElementP.ref -> bad ref
		chain6Head = new CustomRef2();
		LLElementP head3 = new LLElementP();
		chain6Head.ref = head3;
		for (int i = 0; i < 30000; i++) {
			LLElementP next3 = new LLElementP();
			head3.next = next3;
			head3 = next3;
		}
		head3.ref = new Vector(EMPTY_VECTOR6_SIZE);
	}

	private void constructStructsWithBoxedNums() {
		arrayOfUniqueIntegers = new Integer[INTEGER_ARRAY_SIZE];
		for (int i = 0; i < INTEGER_ARRAY_NONNULL_ELEMENTS; i++) {
			arrayOfUniqueIntegers[i] = Integer.valueOf(i); // Don't use normal Integer.valueOf() to ensure uniqueness
		}

		arrayOfShorts = new Short[SHORT_ARRAY_SIZE];
		for (int i = 0; i < SHORT_ARRAY_UNIQUE_NUMS; i++) {
			arrayOfShorts[i] = Short.valueOf((short) i);
		}
		for (int i = SHORT_ARRAY_UNIQUE_NUMS; i < SHORT_ARRAY_SIZE; i++) {
			arrayOfShorts[i] = arrayOfShorts[i % SHORT_ARRAY_UNIQUE_NUMS];
		}

		characterMap = new LinkedHashMap(CHARACTER_MAP_SIZE);
		for (int i = 0; i < CHARACTER_MAP_SIZE; i++) {
			Character c = Character.valueOf((char) i);
			characterMap.put(c, Character.toString(c));
		}
	}

	private void constructSparseCollections() {
		String[] s = new String[NUM_ELS_IN_MAP];
		for (int i = 0; i < 15; i++) {
			s[i] = Integer.toString(i);
		}

		smallSparseHashMaps = new Object[NUM_OF_SMALL_SPARSE_HASHMAPS];
		for (int i = 0; i < NUM_OF_SMALL_SPARSE_HASHMAPS; i++) {
			HashMap map = new HashMap();
			map.put(STR_CONST1, STR_CONST2);
			smallSparseHashMaps[i] = map;
		}

		largeSparseHashMaps = new Object[NUM_OF_SMALL_SPARSE_HASHMAPS];
		for (int i = 0; i < NUM_OF_LARGE_SPARSE_HASHMAPS; i++) {
			HashMap map = new HashMap(LARGE_MAP_EXPLICIT_CAPACITY);
			for (int j = 0; j < NUM_ELS_IN_MAP; j++) {
				map.put(s[j], s[j]);
			}
			largeSparseHashMaps[i] = map;
		}

		smallSparseArrayLists = new Object[NUM_OF_SMALL_SPARSE_ARRAYLISTS];
		for (int i = 0; i < NUM_OF_SMALL_SPARSE_ARRAYLISTS; i++) {
			ArrayList list = new ArrayList();
			list.add(STR_CONST1);
			smallSparseArrayLists[i] = list;
		}

		largeSparseArrayLists = new Object[NUM_OF_LARGE_SPARSE_ARRAYLISTS];
		for (int i = 0; i < NUM_OF_LARGE_SPARSE_ARRAYLISTS; i++) {
			ArrayList list = new ArrayList();
			for (int j = 0; j < NUM_ELS_IN_MAP; j++) {
				list.add(s[j]);
			}
			for (int j = NUM_ELS_IN_MAP - 1; j >= 5; j--) {
				list.remove(j);
			}
			largeSparseArrayLists[i] = list;
		}
	}
}
