1 19 20 package org.netbeans.modules.welcome.content; 21 22 import java.awt.Component ; 23 import java.awt.Dimension ; 24 import java.awt.GridBagConstraints ; 25 import java.awt.GridBagLayout ; 26 import java.awt.Insets ; 27 import java.awt.Toolkit ; 28 import java.awt.event.ActionEvent ; 29 import java.awt.event.ActionListener ; 30 import java.beans.PropertyChangeEvent ; 31 import java.beans.PropertyChangeListener ; 32 import java.io.IOException ; 33 import java.net.SocketException ; 34 import java.net.UnknownHostException ; 35 import java.text.DateFormat ; 36 import java.text.ParseException ; 37 import java.text.SimpleDateFormat ; 38 import java.util.ArrayList ; 39 import java.util.Date ; 40 import java.util.Locale ; 41 import java.util.logging.Level ; 42 import java.util.logging.Logger ; 43 import javax.swing.JButton ; 44 import javax.swing.JComponent ; 45 import javax.swing.JLabel ; 46 import javax.swing.JPanel ; 47 import javax.swing.JScrollPane ; 48 import javax.swing.SwingUtilities ; 49 import javax.xml.parsers.ParserConfigurationException ; 50 import org.openide.ErrorManager; 51 import org.openide.awt.Mnemonics; 52 import org.openide.util.RequestProcessor; 53 import org.openide.util.WeakListeners; 54 import org.openide.xml.XMLUtil; 55 import org.w3c.dom.Node ; 56 import org.xml.sax.Attributes ; 57 import org.xml.sax.ContentHandler ; 58 import org.xml.sax.InputSource ; 59 import org.xml.sax.Locator ; 60 import org.xml.sax.SAXException ; 61 import org.xml.sax.XMLReader ; 62 63 public class RSSFeed extends JScrollPane implements Constants, PropertyChangeListener { 64 65 protected static final int NEWS_COUNT = 10; 66 67 private String url; 68 69 private boolean showProxyButton = true; 70 71 private RequestProcessor.Task reloadTimer; 72 private long lastReload = 0; 73 74 public static final String FEED_CONTENT_PROPERTY = "feedContent"; 75 76 private static DateFormat parsingDateFormat = new SimpleDateFormat ( "EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH ); private static DateFormat parsingDateFormatShort = new SimpleDateFormat ( "EEE, dd MMM yyyy", Locale.ENGLISH ); private static DateFormat printingDateFormat = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT ); 79 private static DateFormat printingDateFormatShort = DateFormat.getDateInstance( DateFormat.SHORT ); 80 81 public RSSFeed( String url, boolean showProxyButton ) { 82 this.url = url; 83 this.showProxyButton = showProxyButton; 84 setBorder(null); 85 setOpaque(false); 86 87 setBackground( Utils.getColor(DEFAULT_BACKGROUND_COLOR) ); 88 getViewport().setBackground( Utils.getColor(DEFAULT_BACKGROUND_COLOR) ); 89 setViewportView( buildContentLoadingLabel() ); 90 91 setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); 92 93 HttpProxySettings.getDefault().addPropertyChangeListener( WeakListeners.propertyChange( this, HttpProxySettings.getDefault() ) ); 94 } 95 96 public RSSFeed( boolean showProxyButton ) { 97 this( null, showProxyButton ); 98 } 99 100 public void setContent( Component content ) { 101 setViewportView( content ); 102 firePropertyChange( FEED_CONTENT_PROPERTY, null, content ); 103 } 104 105 public Component getContent() { 106 return getViewport().getView(); 107 } 108 109 public void reload() { 110 new Reload().start(); 111 } 112 113 protected ArrayList buildItemList() throws SAXException , ParserConfigurationException , IOException { 114 XMLReader reader = XMLUtil.createXMLReader( false, true ); 115 FeedHandler handler = new FeedHandler(); 116 reader.setContentHandler( handler ); 117 reader.setEntityResolver( org.openide.xml.EntityCatalog.getDefault() ); 118 reader.setErrorHandler( new ErrorCatcher() ); 119 120 reader.parse( new InputSource (url) ); 121 122 return handler.getItemList(); 123 } 124 125 126 static class ErrorCatcher implements org.xml.sax.ErrorHandler { 127 private void message(Level level, org.xml.sax.SAXParseException e) { 128 Logger l = Logger.getLogger(RSSFeed.class.getName()); 129 l.log(level, "Line number:"+e.getLineNumber()); l.log(level, "Column number:"+e.getColumnNumber()); l.log(level, "Public ID:"+e.getPublicId()); l.log(level, "System ID:"+e.getSystemId()); l.log(level, "Error message:"+e.getMessage()); } 135 136 public void error(org.xml.sax.SAXParseException e) { 137 message(Level.SEVERE, e); } 139 140 public void warning(org.xml.sax.SAXParseException e) { 141 message(Level.WARNING,e); } 143 144 public void fatalError(org.xml.sax.SAXParseException e) { 145 message(Level.SEVERE,e); } 147 } 149 private class Reload extends Thread { 150 public void run() { 151 try { 152 lastReload = System.currentTimeMillis(); 153 154 ArrayList itemList = buildItemList(); 155 final JPanel contentPanel = new NoHorizontalScrollPanel(); 156 contentPanel.setOpaque( false ); 157 int contentRow = 0; 158 159 Component header = getContentHeader(); 160 if( null != header ) { 161 contentPanel.add( header, new GridBagConstraints (0,contentRow++,1,1,0.0,0.0, 162 GridBagConstraints.CENTER,GridBagConstraints.BOTH, 163 new Insets (0,0,0,0),0,0 ) ); 164 } 165 166 for( int i=0; i<Math.min(itemList.size(), NEWS_COUNT); i++ ) { 167 FeedItem item = (FeedItem)itemList.get(i); 168 169 if( null != item.title && null != item.link ) { 170 JPanel panel = new JPanel ( new GridBagLayout () ); 171 panel.setOpaque( false ); 172 int row = 0; 173 if( item.dateTime != null) { 174 JLabel label = new JLabel (); 175 label.setFont( RSS_DESCRIPTION_FONT ); 176 label.setForeground( Utils.getColor(RSS_DATETIME_COLOR) ); 177 label.setText( formatDateTime( item.dateTime ) ); 178 panel.add( label, new GridBagConstraints (0,row++,1,1,0.0,0.0, 179 GridBagConstraints.WEST,GridBagConstraints.NONE, 180 new Insets (0,TEXT_INSETS_LEFT+5,2,TEXT_INSETS_RIGHT),0,0 ) ); 181 } 182 183 WebLink linkButton = new WebLink( item.title, item.link, true ); 184 linkButton.getAccessibleContext().setAccessibleName( 185 BundleSupport.getAccessibilityName( "WebLink", item.title ) ); linkButton.getAccessibleContext().setAccessibleDescription( 187 BundleSupport.getAccessibilityDescription( "WebLink", item.link ) ); linkButton.setFont( HEADER_FONT ); 189 panel.add( linkButton, new GridBagConstraints (0,row++,1,1,1.0,1.0, 190 GridBagConstraints.WEST,GridBagConstraints.NONE, 191 new Insets (0,5,2,TEXT_INSETS_RIGHT),0,0 ) ); 192 193 194 if (item.description != null) { 195 JLabel label = new JLabel (); 196 label.setFont( RSS_DESCRIPTION_FONT ); 197 label.setText( "<html>"+trimHtml( item.description ) ); panel.add( label, new GridBagConstraints (0,row++,1,1,1.0,1.0, 199 GridBagConstraints.WEST,GridBagConstraints.BOTH, 200 new Insets (0,TEXT_INSETS_LEFT+5,0,TEXT_INSETS_RIGHT),0,0 ) ); 201 } 202 203 contentPanel.add( panel, new GridBagConstraints (0,contentRow++,1,1,1.0,1.0, 204 GridBagConstraints.NORTHWEST,GridBagConstraints.BOTH, 205 new Insets (contentRow==1 ? UNDER_HEADER_MARGIN : 0,0,16,0),0,0 ) ); 206 } 207 } 208 209 SwingUtilities.invokeLater( new Runnable () { 210 public void run() { 211 setContent( contentPanel ); 212 } 213 }); 214 215 reloadTimer = RequestProcessor.getDefault().post( this, RSS_FEED_TIMER_RELOAD_MILLIS ); 217 218 } catch( UnknownHostException uhE ) { 219 SwingUtilities.invokeLater( new Runnable () { 220 public void run() { 221 setContent( buildProxyPanel() ); 222 } 223 }); 224 } catch( SocketException sE ) { 225 SwingUtilities.invokeLater( new Runnable () { 226 public void run() { 227 setContent( buildProxyPanel() ); 228 } 229 }); 230 } catch( IOException ioE ) { 231 SwingUtilities.invokeLater( new Runnable () { 232 public void run() { 233 setContent( buildProxyPanel() ); 234 } 235 }); 236 } catch( Exception e ) { 237 SwingUtilities.invokeLater( new Runnable () { 238 public void run() { 239 setContent( buildErrorLabel() ); 240 } 241 }); 242 ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e ); 243 } 244 } 245 } 246 247 protected static String getTextContent(Node node) { 248 Node child = node.getFirstChild(); 249 if( null == child ) 250 return null; 251 252 return child.getNodeValue(); 253 } 254 255 protected String formatDateTime( String strDateTime ) { 256 try { 257 Date date = parsingDateFormat.parse( strDateTime ); 258 return printingDateFormat.format( date ); 259 } catch( NumberFormatException nfE ) { 260 } catch( ParseException pE ) { 262 try { 263 Date date = parsingDateFormatShort.parse( strDateTime ); 264 return printingDateFormatShort.format( date ); 265 } catch( NumberFormatException nfE ) { 266 } catch( ParseException otherPE ) { 268 } 270 } 271 return strDateTime; 272 } 273 274 private static final long serialVersionUID = 1L; 275 276 public Dimension getPreferredSize() { 277 Dimension retValue = super.getPreferredSize(); 278 retValue.width = 1; 279 retValue.height = 1; 280 return retValue; 281 } 282 283 public void removeNotify() { 284 if( null != reloadTimer ) { 286 reloadTimer.cancel(); 287 reloadTimer = null; 288 } 289 super.removeNotify(); 290 } 291 292 public void addNotify() { 293 super.addNotify(); 294 if( null == reloadTimer && !Boolean.getBoolean("netbeans.full.hack")) { 295 if( System.currentTimeMillis() - lastReload >= RSS_FEED_TIMER_RELOAD_MILLIS ) { 296 reload(); 297 } else { 298 reloadTimer = RequestProcessor.getDefault().post( new Reload(), 299 (int)(RSS_FEED_TIMER_RELOAD_MILLIS - (System.currentTimeMillis() - lastReload)) ); 300 } 301 } 302 } 303 304 private String trimHtml( String htmlSnippet ) { 305 String res = htmlSnippet.replaceAll( "<[^>]*>", "" ); res = res.replaceAll( " ", " " ); res = res.trim(); 308 int maxLen = getMaxDecsriptionLength(); 309 if( res.length() > maxLen ) { 310 res = res.substring( 0, maxLen ) + "..."; } 312 return res; 313 } 314 315 protected int getMaxDecsriptionLength() { 316 int verticalSize = Toolkit.getDefaultToolkit().getScreenSize().height; 317 if( verticalSize >= 1200 ) 318 return 350; 319 if( verticalSize >= 1024 ) 320 return 220; 321 return 140; 322 } 323 324 protected Component getContentHeader() { 325 return null; 326 } 327 328 private JComponent buildProxyPanel() { 329 Component header = getContentHeader(); 330 JPanel panel = null == header ? new JPanel (new GridBagLayout ()) : new NoHorizontalScrollPanel(); 331 panel.setOpaque( false ); 332 333 int row = 0; 334 if( null != header ) { 335 panel.add( header, new GridBagConstraints (0,row++,1,1,1.0,0.0, 336 GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets (0,0,0,0),0,0 ) ); 337 } 338 339 panel.add( new JLabel (BundleSupport.getLabel("ErrCannotConnect")), new GridBagConstraints (0,row++,1,1,0.0,0.0, 341 GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets (5,10,10,5),0,0 ) ); 342 if( showProxyButton ) { 343 JButton button = new JButton (); 344 Mnemonics.setLocalizedText( button, BundleSupport.getLabel( "ProxyConfig" ) ); button.setOpaque( false ); 346 button.addActionListener( new ActionListener () { 347 public void actionPerformed(ActionEvent e) { 348 HttpProxySettings.getDefault().showConfigurationDialog(); 349 } 350 }); 351 panel.add( button, new GridBagConstraints (0,row++,1,1,0.0,0.0, 352 GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets (5,10,10,5),0,0 ) ); 353 } 354 return panel; 355 } 356 357 private JComponent buildContentLoadingLabel() { 358 JLabel label = new JLabel ( BundleSupport.getLabel( "ContentLoading" ) ); label.setHorizontalAlignment( JLabel.CENTER ); 360 label.setVerticalAlignment( JLabel.CENTER ); 361 label.setForeground( Utils.getColor(DEFAULT_TEXT_COLOR) ); 362 label.setBackground( Utils.getColor(DEFAULT_BACKGROUND_COLOR) ); 363 label.setOpaque( false ); 364 Component header = getContentHeader(); 365 if( null != header ) { 366 JPanel panel = new NoHorizontalScrollPanel(); 367 panel.setOpaque( false ); 368 panel.add( header, new GridBagConstraints (0,0,1,1,1.0,1.0, 369 GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets (0,0,0,0),0,0 ) ); 370 panel.add( label, new GridBagConstraints (0,1,1,1,1.0,1.0, 371 GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets (0,0,0,0),0,0 ) ); 372 return panel; 373 } 374 return label; 375 } 376 377 private JComponent buildErrorLabel() { 378 Component header = getContentHeader(); 379 JPanel panel = null == header ? new JPanel (new GridBagLayout ()) : new NoHorizontalScrollPanel(); 380 panel.setOpaque( false ); 381 382 int row = 0; 383 if( null != header ) { 384 panel.add( header, new GridBagConstraints (0,row++,1,1,1.0,0.0, 385 GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets (0,0,0,0),0,0 ) ); 386 } 387 388 panel.add( new JLabel (BundleSupport.getLabel("ErrLoadingFeed")), new GridBagConstraints (0,row++,1,1,0.0,0.0, 390 GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets (5,10,10,5),0,0 ) ); 391 JButton button = new JButton (); 392 Mnemonics.setLocalizedText( button, BundleSupport.getLabel( "Reload" ) ); button.setOpaque( false ); 394 button.addActionListener( new ActionListener () { 395 public void actionPerformed(ActionEvent e) { 396 reload(); 397 } 398 }); 399 panel.add( button, new GridBagConstraints (0,row++,1,1,0.0,0.0, 400 GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets (5,10,10,5),0,0 ) ); 401 return panel; 402 } 403 404 public void propertyChange(PropertyChangeEvent evt) { 405 if( HttpProxySettings.PROXY_SETTINGS.equals( evt.getPropertyName() ) ) { 406 setViewportView( buildContentLoadingLabel() ); 407 reload(); 408 } 409 } 410 411 static class FeedHandler implements ContentHandler { 412 private FeedItem currentItem; 413 private StringBuffer textBuffer; 414 private ArrayList <FeedItem> itemList = new ArrayList <FeedItem>( 10 ); 415 416 public void setDocumentLocator(Locator locator) { 417 } 418 419 public void startDocument() throws SAXException { 420 } 421 422 public void endDocument() throws SAXException { 423 } 424 425 public void startPrefixMapping(String prefix, String uri) throws SAXException { 426 } 427 428 public void endPrefixMapping(String prefix) throws SAXException { 429 } 430 431 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { 432 if( itemList.size() < NEWS_COUNT ) { 433 if( "item".equals( localName ) ) { currentItem = new FeedItem(); 435 } else if( "link".equals( localName ) || "pubDate".equals( localName ) || "date".equals( localName ) || "description".equals( localName ) || "title".equals( localName ) ) { textBuffer = new StringBuffer ( 110 ); 441 } 442 } 443 } 444 445 public void endElement(String uri, String localName, String qName) throws SAXException { 446 if( itemList.size() < NEWS_COUNT ) { 447 if( "item".equals( localName ) ) { if( null != currentItem && currentItem.isValid() ) { 449 itemList.add( currentItem ); 450 } 451 currentItem = null; 452 } else if( null != currentItem && null != textBuffer ) { 453 String text = textBuffer.toString().trim(); 454 textBuffer = null; 455 if( 0 == text.length() ) 456 text = null; 457 458 if( "link".equals( localName ) ) { currentItem.link = text; 460 } else if( "pubDate".equals( localName ) || "date".equals( localName ) ) { currentItem.dateTime = text; 463 } else if( "title".equals( localName ) ) { currentItem.title = text; 465 } else if( "description".equals( localName ) ) { currentItem.description = text; 467 } 468 } 469 } 470 } 471 472 public void characters(char[] ch, int start, int length) throws SAXException { 473 if( null != textBuffer ) 474 textBuffer.append( ch, start, length ); 475 } 476 477 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 478 } 479 480 public void processingInstruction(String target, String data) throws SAXException { 481 } 482 483 public void skippedEntity(String name) throws SAXException { 484 } 485 486 public ArrayList <FeedItem> getItemList() { 487 return itemList; 488 } 489 } 490 491 static class FeedItem { 492 String title; 493 String link; 494 String description; 495 String dateTime; 496 497 boolean isValid() { 498 return null != title && null != link; 499 } 500 } 501 } 502 | Popular Tags |