1 /*
   2  * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package org.openjdk.jigsaw;
  27 
  28 import java.io.*;
  29 import java.lang.module.*;
  30 import java.util.*;
  31 
  32 import static org.openjdk.jigsaw.Repository.ModuleType;
  33 import static org.openjdk.jigsaw.FileConstants.ModuleFile.HashType;
  34 
  35 
  36 /**
  37  * <p> A {@linkplain Repository module repository's} catalog </p>
  38  */
  39 
  40 public abstract class RepositoryCatalog {
  41 
  42     // ## Elements in this class are public only to enable unit tests
  43 
  44     private static final JigsawModuleSystem jms
  45         = JigsawModuleSystem.instance();
  46 
  47     public abstract void gatherDeclaringModuleIds(Set<ModuleId> mids)
  48         throws IOException;
  49     
  50     public abstract void gatherModuleIds(String moduleName, Set<ModuleId> mids)
  51         throws IOException;
  52 
  53     public abstract byte[] readModuleInfoBytes(ModuleId mid)
  54         throws IOException;
  55 
  56     static class Entry {
  57 
  58         final ModuleType type;
  59         final byte[] mibs;
  60         final long csize;
  61         final long usize;
  62         final HashType hashType;
  63         final byte[] hash;
  64 
  65         Entry(ModuleType t, byte[] m, long cs, long us, HashType ht, byte[] h) {
  66             type = t;
  67             mibs = m;
  68             csize = cs;
  69             usize = us;
  70             hashType = ht;
  71             hash = h;
  72         }
  73 
  74     }
  75 
  76     abstract void add(Entry e);
  77 
  78     public void add(ModuleType t, byte[] mibs, long cs, long us,
  79                     HashType hashType, byte[] hash)
  80     {
  81         add(new Entry(t, mibs, cs, us, hashType, hash));
  82     }
  83 
  84     public abstract boolean remove(ModuleId mid);
  85 
  86     abstract Entry get(ModuleId mid);
  87 
  88 
  89     /**
  90      * <p> A {@linkplain RepositoryCatalog repository catalog} which can be
  91      * stored to, and then loaded from, a byte stream </p>
  92      */
  93     public static class StreamedRepositoryCatalog
  94         extends RepositoryCatalog
  95     {
  96 
  97         static final int MAJOR_VERSION = 0;
  98         static final int MINOR_VERSION = 0;
  99 
 100         private Map<ModuleId,Entry> modules = new HashMap<>();
 101         private Map<ModuleId,ModuleId> moduleForViewId= new HashMap<>();
 102 
 103         @Override
 104         public void gatherDeclaringModuleIds(Set<ModuleId> mids) {
 105             mids.addAll(modules.keySet());
 106         }
 107         
 108         @Override
 109         public void gatherModuleIds(String moduleName, Set<ModuleId> mids) {
 110             for (ModuleId mid : moduleForViewId.keySet()) {
 111                 if (moduleName == null || mid.name().equals(moduleName))
 112                     mids.add(mid);
 113             }
 114         }
 115 
 116         @Override
 117         public byte[] readModuleInfoBytes(ModuleId mid) {
 118             Entry e = modules.get(moduleForViewId.get(mid));
 119             return (e != null) ? e.mibs : null;
 120         }
 121 
 122         @Override
 123         void add(Entry e) {
 124             ModuleInfo mi = jms.parseModuleInfo(e.mibs); // ## Need fast path
 125             modules.put(mi.id(), e);
 126             for (ModuleView mv : mi.views()) {
 127                 moduleForViewId.put(mv.id(), mi.id());
 128                 for (ModuleId alias : mv.aliases()) {
 129                     moduleForViewId.put(alias, mi.id());
 130                 }
 131             }
 132         }
 133 
 134         @Override
 135         public boolean remove(ModuleId mid) {
 136             for (Iterator<ModuleId> i = moduleForViewId.values().iterator();
 137                  i.hasNext();)
 138             {
 139                 // remove views/aliases defined in the module be removed
 140                 ModuleId id = i.next();
 141                 if (id.equals(mid)) {
 142                     i.remove();
 143                 }
 144             }
 145             return modules.remove(mid) != null;
 146         }
 147 
 148         @Override
 149         Entry get(ModuleId mid) {
 150             return modules.get(moduleForViewId.get(mid));
 151         }
 152 
 153         /* ##
 154         public boolean remove(ModuleIdQuery midq) {
 155             int nd = 0;
 156             for (Iterator<ModuleId> i = modules.keySet().iterator();
 157                  i.hasNext();)
 158             {
 159                 ModuleId mid = i.next();
 160                 if (midq.matches(mid)) {
 161                     i.remove();
 162                     nd++;
 163                 }
 164             }
 165             return nd != 0;
 166         }
 167         */
 168 
 169         private StreamedRepositoryCatalog() { }
 170 
 171         private FileHeader fileHeader() {
 172             return (new FileHeader()
 173                     .type(FileConstants.Type.STREAM_CATALOG)
 174                     .majorVersion(MAJOR_VERSION)
 175                     .minorVersion(MINOR_VERSION));
 176         }
 177 
 178         public void store(OutputStream os) throws IOException {
 179             OutputStream bos = new BufferedOutputStream(os);
 180             try (DataOutputStream out = new DataOutputStream(bos)) {
 181                 fileHeader().write(out);
 182                 out.writeInt(modules.size());
 183                 for (Map.Entry<ModuleId,Entry> me : modules.entrySet()) {
 184                     out.writeUTF(me.getKey().toString()); // ## Redundant
 185                     Entry e = me.getValue();
 186                     out.writeUTF(e.type.getFileNameExtension());
 187                     out.writeLong(e.csize);
 188                     out.writeLong(e.usize);
 189                     out.writeShort(e.hashType.value());
 190                     out.writeShort(e.hash.length);
 191                     out.write(e.hash);
 192                     out.writeShort(e.mibs.length);
 193                     out.write(e.mibs);
 194                 }
 195                 out.writeInt(moduleForViewId.size());
 196                 for (Map.Entry<ModuleId,ModuleId> me : moduleForViewId.entrySet()) {
 197                     out.writeUTF(me.getKey().toString());
 198                     out.writeUTF(me.getValue().toString());
 199                 }
 200             }
 201         }
 202 
 203         public StreamedRepositoryCatalog loadStream(InputStream is)
 204             throws IOException
 205         {
 206             BufferedInputStream bis = new BufferedInputStream(is);
 207             DataInputStream in = new DataInputStream(bis);
 208             FileHeader fh = fileHeader();
 209             fh.read(in);
 210             int nms = in.readInt();
 211             for (int i = 0; i < nms; i++) {
 212                 ModuleId mid = jms.parseModuleId(in.readUTF());
 213                 ModuleType t = ModuleType.fromFileNameExtension(in.readUTF());
 214                 long cs = in.readLong();
 215                 long us = in.readLong();
 216                 HashType ht = HashType.valueOf(in.readShort());
 217                 int nb = in.readShort();
 218                 byte[] hash = new byte[nb];
 219                 in.readFully(hash);
 220                 nb = in.readShort();
 221                 byte[] mibs = new byte[nb];
 222                 in.readFully(mibs);
 223                 modules.put(mid, new Entry(t, mibs, cs, us, ht, hash));
 224             }
 225             int nmids = in.readInt();
 226             for (int i = 0; i < nmids; i++) {
 227                 ModuleId id = jms.parseModuleId(in.readUTF());
 228                 ModuleId mid = jms.parseModuleId(in.readUTF());
 229                 moduleForViewId.put(id, mid);
 230             }
 231             return this;
 232         }
 233 
 234     }
 235 
 236     public static StreamedRepositoryCatalog load(InputStream in)
 237         throws IOException
 238     {
 239         StreamedRepositoryCatalog src = new StreamedRepositoryCatalog();
 240         if (in != null) {
 241             try {
 242                 src.loadStream(in);
 243             } finally {
 244                 in.close();
 245             }
 246         }
 247         return src;
 248     }
 249 
 250 
 251     /*
 252 
 253     private static class IndexedRepositoryCatalog {  } // ## Later
 254 
 255     static IndexedRepositoryCatalog open(File fn) throws IOException {
 256         return new IndexedRepositoryCatalog(...);
 257     }
 258 
 259     */
 260 
 261 }