/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.openapi.fileTypes.impl;

import com.intellij.ide.FileIconProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.ElementBase;
import com.intellij.ui.DeferredIconImpl;
import com.intellij.util.Function;
import com.intellij.util.ui.update.ComparableObject;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.io.File;
import java.util.*;

/**
 * @author yole
 */
public class NativeFileIconProvider implements FileIconProvider {
  private final JFileChooser myFileChooser = new JFileChooser();
  private final Map<Ext, Icon> myIconCache = new HashMap<Ext, Icon>();
  // on Windows .exe and .ico files provide their own icons which can differ for each file, cache them by full file path
  private final Set<Ext> myCustomIconExtensions =
    SystemInfo.isWindows ? new HashSet<Ext>(Arrays.asList(new Ext("exe"), new Ext("ico"))) : new HashSet<Ext>();
  private final Map<String, Icon> myCustomIconCache = new HashMap<String, Icon>();

  private static final Ext NO_EXT = new Ext(null);

  public Icon getIcon(VirtualFile file, int flags, @Nullable Project project) {
    if (!isNativeFileType(file)) return null;

    final Ext ext = file.getExtension() != null ? new Ext(file.getExtension()) : NO_EXT;
    final String filePath = file.getPath();

    Icon icon;
    synchronized (myIconCache) {
      if (!myCustomIconExtensions.contains(ext)) {
        icon = ext != null ? myIconCache.get(ext) : null;
      }
      else {
        icon = filePath != null ? myCustomIconCache.get(filePath) : null;
      }
    }
    if (icon != null) {
      return icon;
    }
    return new DeferredIconImpl<VirtualFile>(ElementBase.ICON_PLACHOLDER, file, false, new Function<VirtualFile, Icon>() {
      public Icon fun(VirtualFile virtualFile) {
        final File f = new File(filePath);
        if (!f.exists()) {
          return null;
        }
        Icon icon;
        try {
          icon = myFileChooser.getIcon(f);
        }
        catch (Exception e) {      // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4854174
          return null;
        }
        if (ext != null) {
          synchronized (myIconCache) {
            if (!myCustomIconExtensions.contains(ext)) {
              myIconCache.put(ext, icon);
            }
            else if (filePath != null) {
              myCustomIconCache.put(filePath, icon);
            }
          }
        }
        return icon;
      }
    });
  }

  protected boolean isNativeFileType(VirtualFile file) {
    return ElementBase.isNativeFileType(file.getFileType());
  }

  private static class Ext extends ComparableObject.Impl {

    private final Object[] myText;

    private Ext(@Nullable String text) {
      myText = new Object[] {text};
    }

    public Object[] getEqualityObjects() {
      return myText;
    }

    @Override
    public String toString() {
      return myText[0] != null ? myText[0].toString() : null;
    }
  }
}

@@ -1191,73 +1191,8 @@
 r %7B%0A
-  private final JFileChooser myFileChooser = new JFileChooser();%0A
   pr
@@ -2565,28 +2565,197 @@
 ry %7B
-%0A          icon = my
+ // VM will ensure lock to init -static final field--, note we should have no read access here, to avoid deadlock with EDT needed to init component%0A          icon = SwingComponentHolder.our
 File
@@ -3265,24 +3265,142 @@
    %7D);%0A  %7D%0A%0A
+  static class SwingComponentHolder %7B%0A    private static final JFileChooser ourFileChooser = new JFileChooser();%0A  %7D%0A%0A
   protected 

replace init of swing components on component initialization in read action with static inner class access with no read action. this change is needed to avoid hang between read access requires EDT access (to init jcomponent) that might be in write action (IDEA-66030)