View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.platform;
16  
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.lang.reflect.InvocationTargetException;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.commons.lang3.reflect.ConstructorUtils;
25  import org.htmlunit.platform.canvas.rendering.AwtRenderingBackend;
26  import org.htmlunit.platform.canvas.rendering.NoOpRenderingBackend;
27  import org.htmlunit.platform.canvas.rendering.RenderingBackend;
28  import org.htmlunit.platform.font.AwtFontUtil;
29  import org.htmlunit.platform.font.FontUtil;
30  import org.htmlunit.platform.font.NoOpFontUtil;
31  import org.htmlunit.platform.image.ImageData;
32  import org.htmlunit.platform.image.NoOpImageData;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.NamedNodeMap;
35  import org.w3c.dom.Node;
36  
37  /**
38   * Singleton to handle JDK specific stuff.
39   * This is required to support at least the differences with android.
40   *
41   * @author Ronald Brill
42   * @author Sven Strickroth
43   */
44  public final class Platform {
45  
46      // private static final Log LOG = LogFactory.getLog(Platform.class);
47  
48      private static FontUtil FontUtil_;
49  
50      private static XmlUtilsHelperAPI HelperXerces_;
51      private static XmlUtilsHelperAPI HelperSunXerces_;
52  
53      static {
54          try {
55              HelperSunXerces_ = (XmlUtilsHelperAPI) Class.forName("org.htmlunit.platform.util.XmlUtilsSunXercesHelper")
56                                                      .getDeclaredConstructor().newInstance();
57          }
58          catch (final Exception | LinkageError ignored) {
59              // ignore
60          }
61  
62          try {
63              HelperXerces_ = (XmlUtilsHelperAPI) Class.forName("org.htmlunit.platform.util.XmlUtilsXercesHelper")
64                                                      .getDeclaredConstructor().newInstance();
65          }
66          catch (final Exception | LinkageError ignored) {
67              // ignore
68          }
69      }
70  
71      /**
72       * Forward the call to the correct helper.
73       *
74       * @param namedNodeMap the node map
75       * @param attributesOrderMap the order map
76       * @param element the node
77       * @param requiredIndex the required index
78       * @return the index or requiredIndex
79       */
80      public static int getIndex(final NamedNodeMap namedNodeMap, final Map<Integer, List<String>> attributesOrderMap,
81              final Node element, final int requiredIndex) {
82          if (HelperXerces_ != null) {
83              final int result = HelperXerces_.getIndex(namedNodeMap, attributesOrderMap, element, requiredIndex);
84              if (result != -1) {
85                  return result;
86              }
87          }
88          if (HelperSunXerces_ != null) {
89              final int result = HelperSunXerces_.getIndex(namedNodeMap, attributesOrderMap, element, requiredIndex);
90              if (result != -1) {
91                  return result;
92              }
93          }
94  
95          return requiredIndex;
96      }
97  
98      /**
99       * Returns internal Xerces details about all elements in the specified document.
100      * The id of the returned {@link Map} is the {@code nodeIndex} of an element, and the list
101      * is the array of ordered attributes names.
102      * @param document the document
103      * @return the map of an element index with its ordered attribute names
104      */
105     public static Map<Integer, List<String>> getAttributesOrderMap(final Document document) {
106         if (HelperXerces_ != null) {
107             final Map<Integer, List<String>> result = HelperXerces_.getAttributesOrderMap(document);
108             if (result != null) {
109                 return result;
110             }
111         }
112         if (HelperSunXerces_ != null) {
113             final Map<Integer, List<String>> result = HelperSunXerces_.getAttributesOrderMap(document);
114             if (result != null) {
115                 return result;
116             }
117         }
118 
119         return new HashMap<>();
120     }
121 
122     /**
123      * @param imageWidth the width of the image this backend is for
124      * @param imageHeight the height of the image this backend is for
125      * @return a new {@link RenderingBackend}. If the {@link AwtRenderingBackend} can't be used a
126      *         {@link NoOpRenderingBackend} is used instead.
127      */
128     public static RenderingBackend getRenderingBackend(final int imageWidth, final int imageHeight) {
129         // for Android
130         try {
131             final Class<?> backendClass = Class.forName(
132                         "org.htmlunit.platform.canvas.rendering.AwtRenderingBackend");
133             return (RenderingBackend) ConstructorUtils.invokeConstructor(backendClass, imageWidth, imageHeight);
134         }
135         catch (final Throwable e) {
136             return new NoOpRenderingBackend(imageWidth, imageHeight);
137         }
138     }
139 
140     /**
141      * @return a new {@link FontUtil}. If the {@link AwtFontUtil} can't be used a
142      *         {@link NoOpFontUtil} is used instead.
143      */
144     public static FontUtil getFontUtil() {
145         // for Android
146         if (FontUtil_ != null) {
147             return FontUtil_;
148         }
149 
150         try {
151             final Class<?> backendClass = Class.forName("org.htmlunit.platform.font.AwtFontUtil");
152             FontUtil_ = (FontUtil) ConstructorUtils.invokeConstructor(backendClass);
153             return FontUtil_;
154         }
155         catch (final Throwable e) {
156             FontUtil_ = new NoOpFontUtil();
157             return FontUtil_;
158         }
159     }
160 
161     /**
162      * @param inputStream the {@link InputStream} to read from
163      * @return a new {@link ImageData} object constructed from the given inputStream
164      * @throws IOException in case of error
165      */
166     public static ImageData buildImageData(final InputStream inputStream) throws IOException {
167         try {
168             final Class<?> backendClass = Class.forName(
169                         "org.htmlunit.platform.image.ImageIOImageData");
170             return (ImageData) ConstructorUtils.invokeConstructor(backendClass, inputStream);
171         }
172         catch (final InvocationTargetException ex) {
173             final Throwable targetEx = ex.getTargetException();
174             if (targetEx instanceof IOException) {
175                 throw (IOException) targetEx;
176             }
177 
178             return new NoOpImageData();
179         }
180         catch (final RuntimeException ex) {
181             throw ex;
182         }
183         catch (final Throwable ex) {
184             return new NoOpImageData();
185         }
186     }
187 
188     private Platform() {
189         // util class
190     }
191 }