001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.web.core;
043:
044: import java.awt.Graphics;
045: import java.awt.Point;
046: import java.awt.Rectangle;
047: import java.awt.image.BufferedImage;
048: import java.io.IOException;
049: import java.util.Iterator;
050: import java.util.logging.Level;
051: import java.util.logging.Logger;
052: import javax.swing.JComponent;
053: import javax.swing.JEditorPane;
054: import javax.swing.JFrame;
055: import javax.swing.SwingUtilities;
056: import javax.swing.text.AbstractDocument;
057: import javax.swing.text.BadLocationException;
058: import javax.swing.text.Document;
059: import javax.swing.text.EditorKit;
060: import javax.swing.text.View;
061: import org.netbeans.api.lexer.Language;
062: import org.netbeans.api.project.Project;
063: import org.netbeans.api.project.ui.OpenProjects;
064: import org.netbeans.editor.EditorUI;
065: import org.netbeans.editor.Utilities;
066: import org.netbeans.editor.Registry;
067: import org.netbeans.editor.view.spi.EstimatedSpanView;
068: import org.netbeans.editor.view.spi.LockView;
069: import org.netbeans.modules.web.core.palette.JSPPaletteFactory;
070: import org.netbeans.modules.web.spi.webmodule.WebModuleImplementation;
071: import org.openide.text.CloneableEditorSupport;
072: import org.openide.util.Exceptions;
073: import org.openide.util.RequestProcessor;
074:
075: /**
076: * "Warm-up" task for editor. Executed after IDE startup, it should
077: * pre-initialize some suitable parts of the module to improve first time usage
078: * experience - which might suffer from long response time due to class loading
079: * and various initialization.
080: * See {@link org.netbeans.core.AfterStartWarmUp} for details about how the task is run.
081: *
082: * @author Tomas Pavek, Marek Fukala
083: */
084:
085: public class JspEditorWarmUpTask implements Runnable {
086:
087: /**
088: * Number of lines that an artificial document
089: * for view hierarchy code optimization will have.
090: * <br>
091: * The default threshold for hotspot method compilation
092: * is 1500 invocations.
093: */
094: private static final int ARTIFICIAL_DOCUMENT_LINE_COUNT = 1510;
095:
096: /**
097: * Number of times a long document is assigned to the editor pane
098: * which causes the view hierarchy for it to be (re)built.
099: */
100: private static final int VIEW_HIERARCHY_CREATION_COUNT = 1;
101:
102: /**
103: * Width of buffered image area.
104: */
105: private static final int IMAGE_WIDTH = 600;
106:
107: /**
108: * Height of buffered image area.
109: */
110: private static final int IMAGE_HEIGHT = 400;
111:
112: /**
113: * Number of paints to be simulated.
114: */
115: private static final int PAINT_COUNT = 1;
116:
117: private static final boolean debug = Boolean
118: .getBoolean("netbeans.debug.editor.warmup"); // NOI18N
119: // =true;
120:
121: private static final int STATUS_INIT = 0;
122: private static final int STATUS_CREATE_PANE = 1;
123: private static final int STATUS_CREATE_DOCUMENTS = 2;
124: private static final int STATUS_SWITCH_DOCUMENTS = 3;
125: private static final int STATUS_TRAVERSE_VIEWS = 4;
126: private static final int STATUS_RENDER_FRAME = 5;
127: private static final int STATUS_PREINIT_SCHLIEMAN = 6;
128:
129: private int status = STATUS_INIT;
130:
131: private JEditorPane pane;
132: private JFrame frame;
133: private Document emptyDoc;
134: private Document longDoc;
135: private Graphics bGraphics;
136:
137: private EditorKit jspKit;
138:
139: private long startTime;
140:
141: //signals whether the warmuptask has already been performed
142: public static boolean ALREADY_RUN = false;
143:
144: public void run() {
145: switch (status) {
146: case STATUS_INIT:
147: //test whether a WebProject is opened
148: if (!isWebProjectOpened())
149: return;
150:
151: if (debug) {
152: startTime = System.currentTimeMillis();
153: }
154:
155: // Start of a code block that tries to force hotspot to compile
156: // the view hierarchy and related classes for faster performance
157: Iterator componentIterator = Registry
158: .getComponentIterator();
159: if (!componentIterator.hasNext()) { // no components opened yet
160: status = STATUS_CREATE_PANE;
161: SwingUtilities.invokeLater(this ); // must run in AWT
162: } // otherwise stop because editor pane(s) already opened (optimized)
163: break;
164:
165: case STATUS_CREATE_PANE: // now create editor component and assign a kit to it
166: assert SwingUtilities.isEventDispatchThread(); // This part must run in AWT
167:
168: // Init of JSPKit and JSPOptions
169: jspKit = CloneableEditorSupport.getEditorKit("text/x-jsp"); //NOI18N
170:
171: //creating actions instances
172: jspKit.getActions();
173:
174: pane = new JEditorPane();
175: pane.setEditorKit(jspKit);
176:
177: // Obtain extended component (with editor's toolbar and scrollpane)
178: EditorUI editorUI = Utilities.getEditorUI(pane);
179: if (editorUI != null) {
180: // Make sure extended component necessary classes get loaded
181: editorUI.getExtComponent();
182: }
183:
184: Registry.removeComponent(pane);
185:
186: status = STATUS_CREATE_DOCUMENTS;
187: RequestProcessor.getDefault().post(this );
188: break;
189:
190: case STATUS_CREATE_DOCUMENTS:
191:
192: // Have two documents - one empty and another one filled with many lines
193: emptyDoc = jspKit.createDefaultDocument();
194: longDoc = pane.getDocument();
195:
196: try {
197: // Fill the document with data.
198: // Number of lines is more important here than number of columns in a line
199: // Do one big insert instead of many small inserts
200: StringBuffer sb = new StringBuffer();
201: for (int i = ARTIFICIAL_DOCUMENT_LINE_COUNT; i > 0; i--) {
202: sb.append("hello"); // NOI18N
203: }
204: longDoc.insertString(0, sb.toString(), null);
205:
206: status = STATUS_SWITCH_DOCUMENTS;
207: SwingUtilities.invokeLater(this );
208:
209: } catch (BadLocationException e) {
210: Exceptions.printStackTrace(e);
211: }
212: break;
213:
214: case STATUS_SWITCH_DOCUMENTS:
215: // Switch between empty doc and long several times
216: // to force view hierarchy creation
217: for (int i = 0; i < VIEW_HIERARCHY_CREATION_COUNT; i++) {
218: pane.setDocument(emptyDoc);
219:
220: // Set long doc - causes view hierarchy to be rebuilt
221: pane.setDocument(longDoc);
222: }
223:
224: status = STATUS_TRAVERSE_VIEWS;
225: RequestProcessor.getDefault().post(this );
226: break;
227:
228: case STATUS_TRAVERSE_VIEWS:
229: try {
230: // Create buffered image for painting simulation
231: BufferedImage bImage = new BufferedImage(IMAGE_WIDTH,
232: IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
233: bGraphics = bImage.getGraphics();
234: bGraphics.setClip(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
235:
236: // Do view-related operations
237: AbstractDocument doc = (AbstractDocument) pane
238: .getDocument();
239: doc.readLock();
240: try {
241: final View rootView = Utilities
242: .getDocumentView(pane);
243: LockView lockView = LockView.get(rootView);
244: lockView.lock();
245: try {
246: int viewCount = rootView.getViewCount();
247:
248: // Force switch the line views from estimated spans to exact measurements
249: Runnable resetChildrenEstimatedSpans = new Runnable() {
250: public void run() {
251: int cnt = rootView.getViewCount();
252: for (int j = 0; j < cnt; j++) {
253: View v = rootView.getView(j);
254: if (v instanceof EstimatedSpanView) {
255: ((EstimatedSpanView) v)
256: .setEstimatedSpan(false);
257: }
258: }
259: }
260: };
261: if (rootView instanceof org.netbeans.lib.editor.view.GapDocumentView) {
262: ((org.netbeans.lib.editor.view.GapDocumentView) rootView)
263: .renderWithUpdateLayout(resetChildrenEstimatedSpans);
264: } else { // not specialized instance => run normally
265: resetChildrenEstimatedSpans.run();
266: }
267:
268: // Get child allocation for each line
269: for (int j = 0; j < viewCount; j++) {
270: Rectangle alloc = new Rectangle(
271: 0,
272: 0,
273: (int) rootView
274: .getPreferredSpan(View.X_AXIS),
275: (int) rootView
276: .getPreferredSpan(View.Y_AXIS));
277: rootView.getChildAllocation(j, alloc);
278: }
279:
280: // Test modelToView and viewToModel
281: if (false) { // Disabled because of #
282: float rootViewYSpan = rootView
283: .getPreferredSpan(View.Y_AXIS);
284: float maybeLineSpan = rootViewYSpan
285: / viewCount;
286: Point point = new Point();
287: point.x = 5; // likely somewhere inside the first char on the line
288: for (int j = 0; j < viewCount; j++) {
289: pane.modelToView(rootView.getView(j)
290: .getStartOffset());
291:
292: point.y = (int) (j * maybeLineSpan);
293: int pos = pane.viewToModel(point);
294: }
295: }
296:
297: int rootViewWidth = (int) rootView
298: .getPreferredSpan(View.X_AXIS);
299: int rootViewHeight = (int) rootView
300: .getPreferredSpan(View.Y_AXIS);
301: Rectangle alloc = new Rectangle(0, 0,
302: rootViewWidth, rootViewHeight);
303:
304: // Paint into buffered image
305: for (int i = PAINT_COUNT - 1; i >= 0; i--) {
306: rootView.paint(bGraphics, alloc);
307: }
308:
309: } finally {
310: lockView.unlock();
311: }
312: } finally {
313: doc.readUnlock();
314: }
315: } catch (BadLocationException e) {
316: Exceptions.printStackTrace(e);
317: }
318:
319: status = STATUS_RENDER_FRAME;
320: SwingUtilities.invokeLater(this );
321: break;
322:
323: case STATUS_RENDER_FRAME:
324: frame = new JFrame();
325: EditorUI ui = Utilities.getEditorUI(pane);
326: JComponent mainComp = null;
327: if (ui != null) {
328: mainComp = ui.getExtComponent();
329: }
330: if (mainComp == null) {
331: mainComp = new javax.swing.JScrollPane(pane);
332: }
333: frame.getContentPane().add(mainComp);
334: frame.pack();
335: frame.paint(bGraphics);
336: frame.getContentPane().removeAll();
337: frame.dispose();
338: pane.setEditorKit(null);
339:
340: // #45934 - initialize palette here to make first-time
341: // JSP opening faster
342: try {
343: JSPPaletteFactory.getPalette();
344: } catch (IOException e) {
345: Logger.getLogger("global").log(Level.INFO,
346: "Palette per-initialization failed", e);
347: }
348:
349: // Candidates Annotations.getLineAnnotations()
350:
351: if (debug) {
352: System.out.println("View hierarchy initialized: " // NOI18N
353: + (System.currentTimeMillis() - startTime));
354: startTime = System.currentTimeMillis();
355: }
356: break;
357: default:
358: throw new IllegalStateException();
359: }
360: }
361:
362: private static boolean isWebProjectOpened() {
363: //init jasper for all opened projects
364: Project[] openedProjects = OpenProjects.getDefault()
365: .getOpenProjects();
366: for (int i = 0; i < openedProjects.length; i++) {
367: WebModuleImplementation wmImpl = (WebModuleImplementation) openedProjects[i]
368: .getLookup().lookup(WebModuleImplementation.class);
369: if (wmImpl != null)
370: return true;
371: }
372: return false;
373: }
374:
375: }
|