gt:                       mime = new MimeType(type);
context: /*
   * This file is part of VirtualFile.
   *
   * Copyright 2017 by Bernd Riedl <bernd.riedl@gmail.com>
   *
   * Licensed under GNU Lesser General Public License 3.0 or later.
   * Some rights reserved. See COPYING, AUTHORS.
   */
  package at.beris.virtualfile.content.mime;
  
  // JDK imports
  
  import at.beris.virtualfile.content.detect.Detector;
  import at.beris.virtualfile.content.detect.TextDetector;
  import at.beris.virtualfile.content.detect.XmlRootExtractor;
  import at.beris.virtualfile.content.metadata.Metadata;
  
  import javax.xml.namespace.QName;
  import java.io.*;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.nio.file.Files;
  import java.util.*;
  
  /**
   * This class is a MimeType repository. It gathers a set of MimeTypes and
   * enables to retrieves a content-type from its name, from a file name, or from
   * a magic character sequence.
   * <p>
   * The MIME type detection methods that take an {@link InputStream} as
   * an argument will never reads more than {@link #getMinLength()} bytes
   * from the stream. Also the given stream is never
   * {@link InputStream#close() closed}, {@link InputStream#mark(int) marked},
   * or {@link InputStream#reset() reset} by the methods. Thus a client can
   * use the {@link InputStream#markSupported() mark feature} of the stream
   * (if available) to restore the stream back to the state it was before type
   * detection if it wants to process the stream based on the detected type.
   */
  public final class MimeTypes implements Detector, Serializable {
  
      /**
       * Serial version UID.
       */
      private static final long serialVersionUID = -1350863170146349036L;
  
      /**
       * Name of the {@link #rootMimeType root} type, application/octet-stream.
       */
      public static final String OCTET_STREAM = "application/octet-stream";
  
      /**
       * Name of the {@link #textMimeType text} type, text/plain.
       */
      public static final String PLAIN_TEXT = "text/plain";
      
      /**
       * Name of the {@link #xmlMimeType xml} type, application/xml.
       */
      public static final String XML = "application/xml";
  
      /**
       * Root type, application/octet-stream.
       */
      private final MimeType rootMimeType;
      private final List<MimeType> rootMimeTypeL;
  
      /**
       * Text type, text/plain.
       */
      private final MimeType textMimeType;
  
      /*
       * xml type, application/xml
       */
      private final MimeType xmlMimeType;
  
      /**
       * Registered media types and their aliases.
       */
      private final MediaTypeRegistry registry = new MediaTypeRegistry();
  
      /** All the registered MimeTypes indexed on their canonical names */
      private final Map<MediaType, MimeType> types =
          new HashMap<MediaType, MimeType>();
  
      /** The patterns matcher */
      private Patterns patterns = new Patterns(registry);
  
      /** Sorted list of all registered magics */
      private final List<Magic> magics = new ArrayList<Magic>();
  
      /** Sorted list of all registered rootXML */
      private final List<MimeType> xmls = new ArrayList<MimeType>();
  
      public MimeTypes() {
          rootMimeType = new MimeType(MediaType.OCTET_STREAM);
          textMimeType = new MimeType(MediaType.TEXT_PLAIN);
          xmlMimeType = new MimeType(MediaType.APPLICATION_XML);
  
          rootMimeTypeL = Collections.singletonList(rootMimeType);
  
          add(rootMimeType);
          add(textMimeType);
          add(xmlMimeType);
      }
  
      /**
       * Find the Mime Content Type of a document from its name.
       * Returns application/octet-stream if no better match is found.
       *
       * @param name of the document to analyze.
       * @return the Mime Content Type of the specified document name
       */
      public MimeType getMimeType(String name) {
          MimeType type = patterns.matches(name);
          if (type != null) {
              return type;
          }
          type = patterns.matches(name.toLowerCase(Locale.ENGLISH));
          if (type != null) {
              return type;
          } else {
              return rootMimeType;
          }
      }
  
      /**
       * Find the Mime Content Type of a document stored in the given file.
       * Returns application/octet-stream if no better match is found.
       *
       * @param file file to analyze
       * @return the Mime Content Type of the specified document
       * @throws MimeTypeException if the type can't be detected
       * @throws IOException if the file can't be read
       */
      public MimeType getMimeType(File file)
              throws MimeTypeException, IOException {
          try (InputStream inputStream = Files.newInputStream(file.toPath())) {
              return forName(detect(inputStream, new Metadata()).toString());
          }
      }
  
      /**
       * Returns the MIME type that best matches the given first few bytes
       * of a document stream. Returns application/octet-stream if no better
       * match is found.
       * <p>
       * If multiple matches are found, the best (highest priority) matching
       * type is returned. If multiple matches are found with the same priority,
       * then all of these are returned.
       * <p>
       * The given byte array is expected to be at least {@link #getMinLength()}
       * long, or shorter only if the document stream itself is shorter.
       *
       * @param data first few bytes of a document stream
       * @return matching MIME type
       */
      List<MimeType> getMimeType(byte[] data) {
          if (data == null) {
              throw new IllegalArgumentException("Data is missing");
          } else if (data.length == 0) {
              // See https://issues.apache.org/jira/browse/TIKA-483
              return rootMimeTypeL;
          }
  
          // Then, check for magic bytes
          List<MimeType> result = new ArrayList<MimeType>(1);
          int currentPriority = -1;
          for (Magic magic : magics) {
              if (currentPriority > 0 && currentPriority > magic.getPriority()) {
                  break;
              }
              if (magic.eval(data)) {
                  result.add(magic.getType());
                  currentPriority = magic.getPriority();
              }
          }
  
          if (!result.isEmpty()) {
              for (int i=0; i<result.size(); i++) {
                  final MimeType matched = result.get(i);
  
                  // When detecting generic XML (or possibly XHTML),
                  // extract the root element and match it against known types
                  if ("application/xml".equals(matched.getName())
                          || "text/html".equals(matched.getName())) {
                      XmlRootExtractor extractor = new XmlRootExtractor();
  
                      QName rootElement = extractor.extractRootElement(data);
                      if (rootElement != null) {
                          for (MimeType type : xmls) {
                              if (type.matchesXML(
                                      rootElement.getNamespaceURI(),
                                      rootElement.getLocalPart())) {
                                  result.set(i, type);
                                  break;
                              }
                          }
                      } else if ("application/xml".equals(matched.getName())) {
                          // Downgrade from application/xml to text/plain since
                          // the document seems not to be well-formed.
                          result.set(i, textMimeType);
                      }
                  }
              }
              return result;
          }
  
          // Finally, assume plain text if no control bytes are found
          try {
              TextDetector detector = new TextDetector(getMinLength());
              ByteArrayInputStream stream = new ByteArrayInputStream(data);
              MimeType type = forName(detector.detect(stream, new Metadata()).toString());
              return Collections.singletonList(type);
          } catch (Exception e) {
              return rootMimeTypeL;
          }
      }
  
      /**
       * Reads the first {@link #getMinLength()} bytes from the given stream.
       * If the stream is shorter, then the entire content of the stream is
       * returned.
       * <p>
       * The given stream is never {@link InputStream#close() closed},
       * {@link InputStream#mark(int) marked}, or
       * {@link InputStream#reset() reset} by this method.
       *
       * @param stream stream to be read
       * @return first {@link #getMinLength()} (or fewer) bytes of the stream
       * @throws IOException if the stream can not be read
       */
      byte[] readMagicHeader(InputStream stream) throws IOException {
          if (stream == null) {
              throw new IllegalArgumentException("InputStream is missing");
          }
  
          byte[] bytes = new byte[getMinLength()];
          int totalRead = 0;
  
          int lastRead = stream.read(bytes);
          while (lastRead != -1) {
              totalRead += lastRead;
              if (totalRead == bytes.length) {
                  return bytes;
              }
              lastRead = stream.read(bytes, totalRead, bytes.length - totalRead);
          }
  
          byte[] shorter = new byte[totalRead];
          System.arraycopy(bytes, 0, shorter, 0, totalRead);
          return shorter;
      }
  
      /**
       * Returns the registered media type with the given name (or alias).
       * The named media type is automatically registered (and returned) if
       * it doesn't already exist.
       *
       * @param name media type name (case-insensitive)
       * @return the registered media type with the given name or alias
       * @throws MimeTypeException if the given media type name is invalid
       */
      public MimeType forName(String name) throws MimeTypeException {
          MediaType type = MediaType.parse(name);
          if (type != null) {
              MediaType normalisedType = registry.normalize(type);
              MimeType mime = types.get(normalisedType);
  
              if (mime == null) {
                  synchronized (this) {
                     // Double check it didn't already get added while
                     //  we were waiting for the lock
                     mime = types.get(normalisedType);
                     if (mime == null) {
  