1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package sun.management;
26
27import java.lang.management.ThreadInfo;
28import java.util.ArrayList;
29import java.util.HashMap;
30import java.util.List;
31import java.util.Map;
32import java.util.function.Predicate;
33import javax.management.openmbean.ArrayType;
34import javax.management.openmbean.CompositeType;
35import javax.management.openmbean.OpenDataException;
36import javax.management.openmbean.OpenType;
37import javax.management.openmbean.SimpleType;
38import javax.management.openmbean.TabularType;
39import static sun.management.Util.toStringArray;
40
41/**
42 * Provides simplistic support for versioning of {@linkplain CompositeType} instances
43 * based on the latest version and filtering out certain items.
44 */
45final class TypeVersionMapper {
46    private static final class Singleton {
47        private final static TypeVersionMapper INSTANCE = new TypeVersionMapper();
48    }
49
50    final static String V5 = "J2SE 5.0";
51    final static String V6 = "Java SE 6";
52
53    private final Map<String, Map<String, Predicate<String>>> filterMap;
54
55    private TypeVersionMapper() {
56        filterMap = new HashMap<>();
57        setupStackTraceElement();
58        setupThreadInfo();
59    }
60
61    public static TypeVersionMapper getInstance() {
62        return Singleton.INSTANCE;
63    }
64
65    private void setupStackTraceElement() {
66        Map<String, Predicate<String>> filter = new HashMap<>();
67        filterMap.put(StackTraceElement.class.getName(), filter);
68        filter.put(V5, StackTraceElementCompositeData::isV6Attribute);
69        filter.put(V6, StackTraceElementCompositeData::isV6Attribute);
70    }
71
72    private void setupThreadInfo() {
73        Map<String, Predicate<String>> filter = new HashMap<>();
74        filterMap.put(ThreadInfo.class.getName(), filter);
75        filter.put(V5, ThreadInfoCompositeData::isV5Attribute);
76        filter.put(V6, ThreadInfoCompositeData::isV6Attribute);
77    }
78
79    /**
80     * Retrieves the specified version of a {@linkplain CompositeType} instance.
81     * @param type The current (latest) version of {@linkplain CompositeType}
82     * @param version The version identifier (eg. {@linkplain TypeVersionMapper#V5})
83     * @return Returns the {@linkplain CompositeType} corresponding to the requested
84     *         version.
85     * @throws OpenDataException
86     */
87    CompositeType getVersionedCompositeType(CompositeType type, String version)
88        throws OpenDataException
89    {
90        Predicate<String> filter = getFilter(type.getTypeName(), version);
91        if (filter == null) {
92            return type;
93        }
94
95        List<String> itemNames = new ArrayList<>();
96        List<String> itemDesc = new ArrayList<>();
97        List<OpenType<?>> itemTypes = new ArrayList<>();
98
99        for(String item : type.keySet()) {
100            if (filter.test(item)) {
101                itemNames.add(item);
102                itemDesc.add(type.getDescription(item));
103                itemTypes.add(getVersionedType(
104                    type.getType(item),
105                    version
106                ));
107            }
108        }
109        return new CompositeType(
110            type.getTypeName(),
111            version != null ? version + " " + type.getDescription() : type.getDescription(),
112            itemNames.toArray(new String[itemNames.size()]),
113            itemDesc.toArray(new String[itemDesc.size()]),
114            itemTypes.toArray(new OpenType<?>[itemTypes.size()])
115        );
116    }
117
118    private OpenType<?> getVersionedType(OpenType<?> type, String version)
119        throws OpenDataException
120    {
121        if (type instanceof ArrayType) {
122            return getVersionedArrayType((ArrayType)type, version);
123        }
124        if (type instanceof CompositeType) {
125            return getVersionedCompositeType((CompositeType)type, version);
126        }
127        if (type instanceof TabularType) {
128            return getVersionedTabularType((TabularType)type, version);
129        }
130        return type;
131    }
132
133    private ArrayType<?> getVersionedArrayType(ArrayType<?> type, String version)
134        throws OpenDataException
135    {
136        if (type.isPrimitiveArray()) {
137            return type;
138        }
139        OpenType<?> ot = getVersionedType(
140            type.getElementOpenType(),
141            version
142        );
143        if (ot instanceof SimpleType) {
144            return new ArrayType<>((SimpleType<?>)ot, type.isPrimitiveArray());
145        } else {
146            return new ArrayType<>(type.getDimension(), ot);
147        }
148    }
149
150    private TabularType getVersionedTabularType(TabularType type, String version)
151        throws OpenDataException
152    {
153        CompositeType ct = getVersionedCompositeType(
154            type.getRowType(),
155            version
156        );
157
158        if (ct != null) {
159            return new TabularType(
160                type.getTypeName(), type.getDescription(), ct,
161                toStringArray(type.getIndexNames()));
162        }
163        return null;
164    }
165
166    private Predicate<String> getFilter(String type, String version) {
167        Map<String, Predicate<String>> versionMap = filterMap.get(type);
168        if (versionMap == null) {
169            return null;
170        }
171
172        return versionMap.get(version);
173    }
174}
175