
module Java.JAR 
  (readManifest,
   readJAR,
   readMainClass,
   addJAR
  ) where

import Control.Monad.Trans (liftIO)
import qualified Control.Monad.State as St
import Data.List
import qualified Codec.Archive.LibZip as Zip

import Java.ClassPath
import Java.JAR.Archive
import Java.META

readManifest :: Zip.Archive (Maybe Manifest)
readManifest = do
  let manifestPath = "META-INF/MANIFEST.MF"
  files <- Zip.fileNames []
  if manifestPath `elem` files
    then do
         content <- Zip.fileContents [] manifestPath
         case parseMeta content of
           Left e -> fail $ show e
           Right meta -> return $ Just (loadSpec meta)
    else return Nothing

readOne :: FilePath -> String -> Zip.Archive [Tree CPEntry]
readOne jarfile str = do
    files <- Zip.fileNames []
    return $ mapF (NotLoadedJAR jarfile) (buildTree $ filter good files)
  where
    good name = (str `isPrefixOf` name) && (".class" `isSuffixOf` name)

-- | Read MainClass Entry of a MANIFEST.MF file
readMainClass :: FilePath -> IO (Maybe String)
readMainClass jarfile = do
  Zip.withArchive [] jarfile $ do
    m <- readManifest
    case m of
      Nothing -> return Nothing
      Just mf -> return $ mainClass mf

-- | Read entries from JAR file, using MANIFEST.MF if it exists.
readJAR :: FilePath -> IO [Tree CPEntry]
readJAR jarfile = do
  r <- Zip.withArchive [] jarfile $ do
         m <- readManifest
         case m of
           Nothing -> return Nothing
           Just mf -> do
                      trees <- mapM (readOne jarfile) (map meName $ manifestEntries mf)
                      let forest = merge (concat trees)
                      return (Just forest)
  case r of
    Nothing -> readAllJAR jarfile
    Just [] -> readAllJAR jarfile
    Just f  -> return f

-- | Add given JAR file to ClassPath
addJAR :: FilePath -> ClassPath ()
addJAR jarfile = do
  classes <- liftIO $ readJAR jarfile
  cp <- St.get
  let cp' = merge $ cp ++ classes
  St.put cp'

