Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 19833 Details for
Bug 91221
Code assist stopped working
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
The file
TypeInfoViewer2.java (text/plain), 23.53 KB, created by
Dirk Baeumer
on 2005-04-12 17:56:56 EDT
(
hide
)
Description:
The file
Filename:
MIME Type:
Creator:
Dirk Baeumer
Created:
2005-04-12 17:56:56 EDT
Size:
23.53 KB
patch
obsolete
>/******************************************************************************* > * Copyright (c) 2000, 2005 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at > * http://www.eclipse.org/legal/epl-v10.html > * > * Contributors: > * IBM Corporation - initial API and implementation > *******************************************************************************/ > package org.eclipse.jdt.internal.ui.dialogs; > >import java.util.ArrayList; >import java.util.Arrays; >import java.util.Comparator; >import java.util.HashSet; >import java.util.List; >import java.util.Set; > >import org.eclipse.core.runtime.IProgressMonitor; >import org.eclipse.core.runtime.IStatus; >import org.eclipse.core.runtime.OperationCanceledException; >import org.eclipse.core.runtime.Status; >import org.eclipse.core.runtime.jobs.Job; > >import org.eclipse.swt.SWT; >import org.eclipse.swt.events.ControlEvent; >import org.eclipse.swt.events.ControlListener; >import org.eclipse.swt.events.KeyAdapter; >import org.eclipse.swt.events.KeyEvent; >import org.eclipse.swt.graphics.GC; >import org.eclipse.swt.graphics.Image; >import org.eclipse.swt.graphics.Rectangle; >import org.eclipse.swt.widgets.Composite; >import org.eclipse.swt.widgets.Event; >import org.eclipse.swt.widgets.Table; >import org.eclipse.swt.widgets.TableItem; > >import org.eclipse.jface.viewers.LabelProvider; > >import org.eclipse.jdt.core.Flags; >import org.eclipse.jdt.core.JavaModelException; >import org.eclipse.jdt.core.search.IJavaSearchConstants; >import org.eclipse.jdt.core.search.SearchEngine; >import org.eclipse.jdt.core.search.SearchPattern; >import org.eclipse.jdt.core.search.TypeNameRequestor; > >import org.eclipse.jdt.internal.corext.util.Strings; >import org.eclipse.jdt.internal.corext.util.TypeInfo; >import org.eclipse.jdt.internal.corext.util.TypeInfoFactory; >import org.eclipse.jdt.internal.corext.util.TypeInfoHistory; > >import org.eclipse.jdt.ui.JavaElementLabels; > >import org.eclipse.jdt.internal.ui.JavaPlugin; >import org.eclipse.jdt.internal.ui.JavaPluginImages; >import org.eclipse.jdt.internal.ui.JavaUIMessages; >import org.eclipse.jdt.internal.ui.util.StringMatcher; > >public class TypeInfoViewer2 { > > private static class TypeInfoFilter { > private boolean fIgnoreCase= true; > > private StringMatcher fPackageMatcher; > private String fPackagePattern; > > private String fNamePattern; > private StringMatcher fNameMatcher; > > private String fPostQualifier; > > private static final char END_SYMBOL= '<'; > private static final char ANY_STRING= '*'; > > public TypeInfoFilter(String pattern) { > int index= pattern.lastIndexOf("-"); //$NON-NLS-1$ > if (index != -1) { > fPostQualifier= pattern.substring(index + 1).trim(); > pattern= pattern.substring(0, index); > } > index= pattern.lastIndexOf("."); //$NON-NLS-1$ > if (index == -1) { > fNamePattern= createPattern(pattern); > } else { > fPackagePattern= pattern.substring(0, index); > fNamePattern= createPattern(pattern.substring(index + 1)); > fPackageMatcher= new StringMatcher(fPackagePattern, fIgnoreCase, false); > } > fNameMatcher= new StringMatcher(fNamePattern, fIgnoreCase, false); > } > public String getPackagePattern() { > return fPackagePattern; > } > public String getNamePattern() { > return fNamePattern; > } > public int getSearchFlags() { > int result= 0; > if (!fIgnoreCase) { > result= result | SearchPattern.R_CASE_SENSITIVE; > } > if (fNamePattern.indexOf("*") != -1) { //$NON-NLS-1$ > result= result | SearchPattern.R_PATTERN_MATCH; > } > return result; > } > public boolean matchHistroyElement(TypeInfo info) { > if (!fNameMatcher.match(info.getTypeName())) > return false; > > if (fPackageMatcher == null) > return true; > > return fPackageMatcher.match(info.getTypeContainerName()); > } > public boolean matchPostQualification(String label) { > if (fPostQualifier == null) > return true; > return label.endsWith(fPostQualifier); > } > private String createPattern(final String text) { > int length= text.length(); > String result= text; > if (length > 0) { > if (length > 1) { > char last= text.charAt(0); > if (Character.isUpperCase(last)) { > StringBuffer buffer= new StringBuffer(); > buffer.append(last); > for (int i= 1; i < length; i++) { > char c= text.charAt(i); > if (Character.isUpperCase(last) && Character.isUpperCase(c)) { > buffer.append("*"); //$NON-NLS-1$ > } > buffer.append(c); > last= c; > } > String s= buffer.toString(); > if (!text.equals(s)) { > fIgnoreCase= false; > result= s; > } > } > } > length= result.length(); > switch (result.charAt(length - 1)) { > case END_SYMBOL: > result= result.substring(0, length - 1); > break; > case ANY_STRING: > break; > default: > result= result + ANY_STRING; > } > } > return result; > } > } > > private static class Data { > public int modifier; > public char[] name; > public char[] packageName; > public char[][] enclosingNames; > public String path; > public Data(int modifier, char[] name, char[] packageName, char[][] enclosingNames, String path) { > super(); > this.modifier= modifier; > this.name= name; > this.packageName= packageName; > this.enclosingNames= enclosingNames; > this.path= path; > } > } > > /* > * No need to synchronize this class. The synchronization takes place via the > * add/done/cancel/failure methods on the viewer. > */ > private static class SearchRequestor extends TypeNameRequestor { > private List fResult; > > private TypeInfoFactory factory= new TypeInfoFactory(); > private volatile boolean fStop; > private Set fHistory; > > public SearchRequestor() { > super(); > fResult= new ArrayList(500); > } >// public TypeInfo[] getResult() { >// return (TypeInfo[])fResult.toArray(new TypeInfo[fResult.size()]); >// } > public Data[] getResult() { > return (Data[])fResult.toArray(new Data[fResult.size()]); > } > public void cancel() { > fStop= true; > } > public void setHistory(Set history) { > fHistory= history; > } > public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) { > if (fStop) > return; > fResult.add(new Data(modifiers, simpleTypeName, packageName, enclosingTypeNames, path)); >// TypeInfo info= factory.create(packageName, simpleTypeName, enclosingTypeNames, modifiers, path); >// if (!fHistory.contains(info) && !TypeFilter.isFiltered(packageName, simpleTypeName)) >// fResult.add(info); > } > } > > private static class TypeInfoComparator implements Comparator { > public int compare(Object left, Object right) { > TypeInfo leftInfo= (TypeInfo)left; > TypeInfo rightInfo= (TypeInfo)right; > int result= compareName(leftInfo.getTypeName(), rightInfo.getTypeName()); > if (result == 0) { > return comparePackageName(leftInfo.getPackageName(), rightInfo.getPackageName()); > } > return result; > } > private int compareName(String leftString, String rightString) { > int result= leftString.compareToIgnoreCase(rightString); > if (result != 0) { > return result; > } else if (Strings.isLowerCase(leftString.charAt(0)) && > !Strings.isLowerCase(rightString.charAt(0))) { > return +1; > } else if (Strings.isLowerCase(rightString.charAt(0)) && > !Strings.isLowerCase(leftString.charAt(0))) { > return -1; > } else { > return leftString.compareTo(rightString); > } > } > private int comparePackageName(String leftString, String rightString) { > int leftLength= leftString.length(); > int rightLength= rightString.length(); > if (leftLength == 0 && rightLength > 0) > return -1; > if (leftLength == 0 && rightLength == 0) > return 0; > if (leftLength > 0 && rightLength == 0) > return +1; > return compareName(leftString, rightString); > } > } > > private static class TypeInfoLabelProvider extends LabelProvider { > > private static final Image CLASS_ICON= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_CLASS); > private static final Image ANNOTATION_ICON= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_ANNOTATION); > private static final Image INTERFACE_ICON= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_INTERFACE); > private static final Image ENUM_ICON= JavaPluginImages.get(JavaPluginImages.IMG_OBJS_ENUM); > > public String getText(Object element) { > return getText(element, false); > } > public String getText(Object element, boolean postQualify) { > TypeInfo info= (TypeInfo)element; > StringBuffer result= new StringBuffer(); > result.append(info.getTypeName()); > String container= info.getTypeContainerName(); > if (container != null && container.length() > 0) { > result.append(JavaElementLabels.CONCAT_STRING); > result.append(container); > } > if (postQualify) { > result.append(JavaElementLabels.CONCAT_STRING); > result.append(info.getPackageFragmentRootPath().toString()); > } else { > } > return result.toString(); > } > public Image getImage(Object element) { > int modifiers= ((TypeInfo)element).getModifiers(); > if (Flags.isAnnotation(modifiers)) { > return ANNOTATION_ICON; > } else if (Flags.isEnum(modifiers)) { > return ENUM_ICON; > } else if (Flags.isInterface(modifiers)) { > return INTERFACE_ICON; > } > return CLASS_ICON; > } > } > > private static class SearchJob extends Job { > private TypeInfoViewer2 fViewer; > private TypeInfoLabelProvider fLabelProvider; > > > private TypeInfo[] fHistory; > private TypeInfoFilter fFilter; > > private SearchRequestor fReqestor; > > public SearchJob(TypeInfoViewer2 viewer, TypeInfoFilter filter, TypeInfo[] history, int numberOfVisibleItems) { > super(JavaUIMessages.getString("TypeInfoViewer.job.label")); //$NON-NLS-1$ > fViewer= viewer; > fLabelProvider= fViewer.getLabelProvider(); > fFilter= filter; > fHistory= history; > > fReqestor= new SearchRequestor(); > setSystem(true); > } > public void stop() { > fReqestor.cancel(); > cancel(); > } > protected IStatus run(IProgressMonitor monitor) { > if (monitor.isCanceled()) { > return canceled(null, false); > } > fViewer.clear(); > Set fFilteredHistory= new HashSet(); > List elements= new ArrayList(); > List images= new ArrayList(); > List labels= new ArrayList(); > for (int i= 0; i < fHistory.length; i++) { > TypeInfo type= fHistory[i]; > if (fFilter.matchHistroyElement(type)) { > fFilteredHistory.add(type); > elements.add(type); > images.add(fLabelProvider.getImage(type)); > labels.add(fLabelProvider.getText(type)); > } > } > fViewer.addAll(elements, images, labels); > fReqestor.setHistory(fFilteredHistory); > long start= System.currentTimeMillis(); > SearchEngine engine= new SearchEngine(); > try { > String packPattern= fFilter.getPackagePattern(); > engine.searchAllTypeNames( > packPattern == null ? null : packPattern.toCharArray(), > fFilter.getNamePattern().toCharArray(), > fFilter.getSearchFlags(), > IJavaSearchConstants.TYPE, > SearchEngine.createWorkspaceScope(), > fReqestor, > IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, > monitor); > } catch (OperationCanceledException e) { > return canceled(e, false); > } catch (JavaModelException e) { > fViewer.failed(e); > return new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.ERROR, JavaUIMessages.getString("TypeInfoViewer.job.error"), e); //$NON-NLS-1$ > } > System.out.println("Time needed until search has finished: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ > Data[] datas= fReqestor.getResult(); > TypeInfoFactory factory= new TypeInfoFactory(); > TypeInfo[] result= new TypeInfo[datas.length]; > for (int i= 0; i < datas.length; i++) { > Data data= datas[i]; > result[i]= factory.create(data.packageName, data.name, data.enclosingNames, data.modifier, data.path); > } > System.out.println("Time needed until type info has been created: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ > Arrays.sort(result, new TypeInfoComparator()); > System.out.println("Time needed until sort has finished: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ > if (monitor.isCanceled()) > return canceled(null, false); > if (result.length > 0 && fFilteredHistory.size() > 0) { > fViewer.addDashLine(); > } > int currentIndex= 0; > String lastLabel= null; > String nextLabel= null; > while (true) { > long startTime= System.currentTimeMillis(); > elements.clear(); > images.clear(); > labels.clear(); > int delta = Math.min(currentIndex == 0 ? fViewer.getNumberOfVisibleItems() : 10, result.length - currentIndex); > if (delta == 0) > break; > int end= currentIndex + delta; > for (int i= currentIndex; i < end; i++) { > if (monitor.isCanceled()) > return canceled(null, false); > TypeInfo type= result[i]; > String label= nextLabel == null ? fLabelProvider.getText(type, false) : nextLabel; > if (!fFilter.matchPostQualification(label)) > continue; > if (label.equals(lastLabel)) { > lastLabel= label; > nextLabel= null; > label= fLabelProvider.getText(type, true); > } else { > lastLabel= label; > if (i + 1 < result.length) { > TypeInfo next= result[i + 1]; > nextLabel= fLabelProvider.getText(next, false); > if (label.equals(nextLabel)) { > label= fLabelProvider.getText(type, true); > } > } else { > nextLabel= null; > } > } > labels.add(label); > elements.add(type); > images.add(fLabelProvider.getImage(type)); > } > fViewer.addAll(elements, images, labels); > currentIndex= end; > try { > long sleep= 100 - (System.currentTimeMillis() - startTime); > if (DEBUG) { > System.out.println("Sleeping for: " + sleep); //$NON-NLS-1$ > } > if (sleep > 0) > Thread.sleep(sleep); > } catch (InterruptedException e) { > return canceled(e, true); > } > if (monitor.isCanceled()) > return canceled(null, false); > } > fViewer.done(); > return new Status(IStatus.OK, JavaPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$ > } > private IStatus canceled(Exception e, boolean removePendingItems) { > fViewer.canceled(removePendingItems); > return new Status(IStatus.CANCEL, JavaPlugin.getPluginId(), IStatus.CANCEL, JavaUIMessages.getString("TypeInfoViewer.job.cancel"), e); //$NON-NLS-1$ > } > } > > private static class ProgressMonitor implements IProgressMonitor { > private boolean fCanceled; > private String fName; > private int fTotalWork; > private TypeInfoViewer2 fViewer; > private long fStarted= -1; > public void beginTask(String name, int totalWork) { > fName= name; > fTotalWork= totalWork; > fStarted= System.curr<code assist>; > } > public void setTaskName(String name) { > } > public void subTask(String name) { > } > public void worked(int work) { > } > public void done() { > fViewer.progressDone(); > } > public void internalWorked(double work) { > } > public void setCanceled(boolean value) { > fCanceled= value; > } > public boolean isCanceled() { > return fCanceled; > } > } > > private static class SyncJob extends Job { > public SyncJob() { > super("Syncronizing types cache"); > } > protected IStatus run(IProgressMonitor monitor) { > try { > new SearchEngine().searchAllTypeNames( > null, > // make sure we search a concrete name. This is faster according to Kent > "_______________".toCharArray(), //$NON-NLS-1$ > SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, > IJavaSearchConstants.CLASS, > SearchEngine.createWorkspaceScope(), > new TypeNameRequestor() {}, > IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, > monitor); > } catch (JavaModelException e) { > } catch (OperationCanceledException e) { > } > return new Status(IStatus.OK, JavaPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$ > } > } > > > private Table fTable; > private TypeInfoLabelProvider fLabelProvider; > > private int fNumberOfVisibleItems; > private int fNextElement; > private List fItems; > > private TypeInfoHistory fHistory; > > private SearchJob fSearchJob; > > private TableItem fProgressItem; > > // private static final char MDASH= ''; > // private static final char MDASH= '\u2012'; // figure dash > // private static final char MDASH= '\u2013'; // en dash > private static final char MDASH= '\u2014'; // em dash > // private static final char MDASH= '\u2015'; // horizontal bar > > private static final boolean DEBUG= false; > > private static class DashLine { > public int fCharWidth; > public String getText(int width) { > StringBuffer buffer= new StringBuffer(); > for (int i= 0; i < width / fCharWidth; i++) { > buffer.append(MDASH); > } > return buffer.toString(); > } > public void setCharWidth(int width) { > fCharWidth= width; > } > } > private DashLine fDashLine= new DashLine(); > > public TypeInfoViewer2(Composite parent) { > fTable= new Table(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.FLAT); > fLabelProvider= new TypeInfoLabelProvider(); > fItems= new ArrayList(500); > fTable.setHeaderVisible(false); > fTable.addControlListener(new ControlListener() { > public void controlResized(ControlEvent event) { > int itemHeight= fTable.getItemHeight(); > Rectangle clientArea= fTable.getClientArea(); > fNumberOfVisibleItems= (clientArea.height / itemHeight) + 1; > } > public void controlMoved(ControlEvent e) { > } > }); > GC gc= null; > try { > gc= new GC(fTable); > fDashLine.setCharWidth(gc.getCharWidth(MDASH)); > } finally { > gc.dispose(); > } > fTable.addKeyListener(new KeyAdapter() { > public void keyPressed(KeyEvent e) { > if (e.keyCode == SWT.DEL) { > int index= fTable.getSelectionIndex(); > if (index == -1) > return; > TableItem item= fTable.getItem(index); > Object element= item.getData(); > if (!(element instanceof TypeInfo)) > return; > if (fHistory.remove((TypeInfo)element) != null) { > item.dispose(); > int count= fTable.getItemCount(); > if (count > 0) { > if (index >= count) { > index= count - 1; > } > fTable.setSelection(index); > fTable.notifyListeners(SWT.Selection, new Event()); > } > } > } > > } > }); > fHistory= TypeInfoHistory.getInstance(); > reset(); > } > > public Table getTable() { > return fTable; > } > > /* Method is called from withing the UI thread */ > public void stop(boolean join) { > if (fSearchJob != null) { > fSearchJob.stop(); > if (join) { > boolean joined= false; > while (!joined) { > try { > fSearchJob.join(); > joined= true; > } catch (InterruptedException e) { > joined= false; > } > } > } > } > } > > /* Method is called from withing the UI thread */ > public TypeInfo[] getSelection() { > TableItem[] items= fTable.getSelection(); > List result= new ArrayList(items.length); > for (int i= 0; i < items.length; i++) { > Object data= items[i].getData(); > if (data instanceof TypeInfo) { > result.add(data); > } > } > return (TypeInfo[])result.toArray(new TypeInfo[result.size()]); > } > > /* Method is called from withing the UI thread */ > public void setSearchPattern(String text) { > stop(true); > if (text.length() == 0 || "*".equals(text)) { //$NON-NLS-1$ > reset(); > } else { > fSearchJob= new SearchJob(this, new TypeInfoFilter(text), fHistory.getTypeInfos(), fNumberOfVisibleItems); > fSearchJob.schedule(); > } > } > > public void setFocus() { > fTable.setFocus(); > } > > private TypeInfoLabelProvider getLabelProvider() { > return fLabelProvider; > } > > private int getNumberOfVisibleItems() { > return fNumberOfVisibleItems; > } > > private void clear() { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > fNextElement= 0; > } > }); > } > > private void addDashLine() { > add(fDashLine); > } > > private void add(final Object element) { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > addSingleElement(element); > } > }); > } > > private void addSingleElement(final Object element) { > TableItem item= null; > if (fItems.size() > fNextElement) { > item= (TableItem)fItems.get(fNextElement); > } else { > item= new TableItem(fTable, SWT.NONE); > fItems.add(item); > } > fillItem(item, element); > item.setData(element); > fNextElement++; > if (fNextElement == 1) { > fTable.setSelection(0); > } > } > > private void addAll(final List elements, final List images, final List labels) { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > int size= elements.size(); > for(int i= 0; i < size; i++) { > addSingleElement(elements.get(i), (Image)images.get(i), (String)labels.get(i)); > } > } > }); > } > > private void addSingleElement(Object element, Image image, String label) { > TableItem item= null; > if (fItems.size() > fNextElement) { > item= (TableItem)fItems.get(fNextElement); > } else { > item= new TableItem(fTable, SWT.NONE); > fItems.add(item); > } > item.setImage(image); > item.setText(label); > item.setData(element); > fNextElement++; > if (fNextElement == 1) { > fTable.setSelection(0); > } > } > > private void reset() { > fNextElement= 0; > TypeInfo[] historyItems= fHistory.getTypeInfos(); > for (int i= 0; i < historyItems.length; i++) { > add(historyItems[i]); > } > shortenTable(); > } > > private void canceled(final boolean removePendingItems) { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > if (removePendingItems) { > shortenTable(); > } > fSearchJob= null; > } > }); > } > > private void done() { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > shortenTable(); > fSearchJob= null; > } > }); > } > > private synchronized void failed(JavaModelException e) { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > shortenTable(); > fSearchJob= null; > } > }); > } > > private void showProgress(final String text) { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > if (fTable.isDisposed()) > return; > if (fProgressItem == null) { > fProgressItem= new TableItem(fTable, SWT.NONE); > } > fProgressItem.setText(text); > } > }); > } > > private void progressDone() { > fTable.getDisplay().syncExec(new Runnable() { > public void run() { > if (fTable.isDisposed()) > return; > if (fProgressItem != null) { > fProgressItem.dispose(); > fProgressItem= null; > } > } > }); > } > > private void fillItem(TableItem item, Object element) { > if (element instanceof DashLine) { > Rectangle bounds= item.getImageBounds(0); > Rectangle clientArea= fTable.getClientArea(); > item.setText(fDashLine.getText(clientArea.width - bounds.x - bounds.width - 20)); > item.setImage((Image)null); > item.setGrayed(true); > } else { > item.setImage(fLabelProvider.getImage(element)); > item.setText(fLabelProvider.getText(element)); > } > item.setData(element); > } > > private void shortenTable() { > if (fNextElement < fItems.size()) { > fTable.setRedraw(false); > fTable.remove(fNextElement, fItems.size() - 1); > fTable.setRedraw(true); > } > for (int i= fItems.size() - 1; i >= fNextElement; i--) { > fItems.remove(i); > } > } >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 91221
: 19833